Перейти к основному содержимому

5.03. Справочник по конфигурациям в Java

Разработчику Архитектору

Справочник по конфигурациям в Java

1. JVM-аргументы запуска (java [options] …)

Все JVM-аргументы подразделяются на три группы:

  • -D<property>=<value> — установка системных свойств;
  • -X — нестандартные опции (non-standard), могут отличаться между реализациями JVM;
  • -XX — экспериментальные и внутренние опции управления поведением JVM.

1.1. Системные свойства (-D)

Свойства, устанавливаемые при старте JVM через -Dkey=value, становятся доступны через System.getProperty("key"). Некоторые из них влияют на поведение стандартных библиотек или JVM.

СвойствоОписаниеВозможные значенияПример
java.versionВерсия Java Runtime Environment (только для чтения)Строка, например 17.0.12
java.homeПуть к установке JREАбсолютный путь/usr/lib/jvm/java-17-openjdk
java.class.pathПуть кисходным классам и JAR-файламСписок путей, разделённых : (Unix) или ; (Windows)-cp lib/*:app.jar
java.library.pathПоиск нативных библиотек (.so, .dll, .dylib) при вызове System.loadLibraryСписок путей-Djava.library.path=/opt/native
java.io.tmpdirКаталог временных файловПуть к директории/tmp, C:\Temp
file.encodingКодировка по умолчанию для операций ввода-выводаUTF-8, ISO-8859-1, Windows-1251 и др.-Dfile.encoding=UTF-8
sun.stdout.encoding, sun.stderr.encodingКодировка потоков System.out и System.errТа же, что file.encoding, но можно задать отдельно-Dsun.stdout.encoding=UTF-8
user.language, user.country, user.variantЛокаль по умолчаниюen, ru; US, RU; POSIX, WIN-Duser.language=ru -Duser.country=RU
user.timezoneЧасовой пояс по умолчаниюUTC, Europe/Moscow, Asia/Yekaterinburg-Duser.timezone=Europe/Moscow
http.proxyHost, http.proxyPort, https.proxyHost, https.proxyPortНастройка HTTP/HTTPS-проксиХост и порт-Dhttp.proxyHost=proxy.local -Dhttp.proxyPort=3128
http.nonProxyHostsХосты, к которым прокси не применяетсяСписок имён, разделённых `, возможны *и?`
socksProxyHost, socksProxyPortSOCKS-проксиХост и порт-DsocksProxyHost=127.0.0.1 -DsocksProxyPort=1080
java.net.preferIPv4StackИспользовать IPv4-стек при наличии дуального стекаtrue, false-Djava.net.preferIPv4Stack=true
java.net.preferIPv6AddressesПредпочитать IPv6-адреса при разрешении имёнtrue, false-Djava.net.preferIPv6Addresses=true
networkaddress.cache.ttlTTL кэша DNS-имён (в секундах), положительное число0 (никогда не кэшировать), -1 (кэшировать навсегда), 30, 60, и т.д.-Dnetworkaddress.cache.ttl=30
networkaddress.cache.negative.ttlTTL для отрицательных DNS-ответовто же-Dnetworkaddress.cache.negative.ttl=10
jdk.tls.client.protocols, jdk.tls.server.protocolsРазрешённые TLS-протоколыTLSv1.2, TLSv1.3, TLSv1,TLSv1.1,TLSv1.2-Djdk.tls.client.protocols=TLSv1.2,TLSv1.3
jdk.tls.disabledAlgorithmsАлгоритмы, запрещённые для TLSСписок, как в java.security (см. ниже)-Djdk.tls.disabledAlgorithms=SSLv3, RC4, MD5
javax.net.ssl.trustStore, javax.net.ssl.trustStorePassword, javax.net.ssl.keyStore, javax.net.ssl.keyStorePasswordПути и пароли к хранилищам сертификатовПуть к файлу JKS/PKCS12, пароль-Djavax.net.ssl.trustStore=trust.jks -Djavax.net.ssl.trustStorePassword=changeit
jdk.serialFilterГлобальный фильтр десериализации (Java ≥ 9)Паттерн в формате pattern;pattern;…-Djdk.serialFilter="!*" (запрет всех), com.example.*;maxdepth=5
java.util.concurrent.ForkJoinPool.common.parallelismРазмер пула потоков ForkJoinPool.commonPool()Целое число ≥ 1-Djava.util.concurrent.ForkJoinPool.common.parallelism=4
sun.java2d.openglИспользовать OpenGL для Java2Dtrue, false, True, False-Dsun.java2d.opengl=True
awt.useSystemAAFontSettingsСглаживание шрифтов в AWT/Swingon, off, gasp, lcd, lcd_hrgb, lcd_vrgb-Dawt.useSystemAAFontSettings=lcd
swing.aatextСглаживание шрифтов в Swingtrue, false-Dswing.aatext=true
sun.java.launcher.pidPID родительского процесса-запускателя (только чтение)Целое число

⚠️ Свойства, начинающиеся с sun.* или com.sun.*, не являются частью официальной спецификации Java SE. Их поведение может меняться между реализациями (OpenJDK, Oracle JDK, Azul, IBM Semeru и др.).


1.2. Нестандартные -X параметры

Эти параметры реализованы в OpenJDK и совместимых сборках. Часть из них стабильна, часть устарела или удалена в новых версиях.

ПараметрОписаниеЗначения / Примеры
-Xms<size>Начальный размер heap-памяти-Xms256m, -Xms1g, -Xms4096m
-Xmx<size>Максимальный размер heap-памяти-Xmx2g, -Xmx8192m
-Xss<size>Размер стека одного потока-Xss512k, -Xss1m (по умолчанию: ~1 МБ на x64)
-Xmn<size>Размер молодого поколения (young generation)-Xmn1g — эквивалентно -XX:NewSize=1g -XX:MaxNewSize=1g
-XnoclassgcОтключение сборки мусора классов (устаревшее, игнорируется в новых версиях)
-Xverify:<mode>Проверка байткода (устарело в Java 13+)remote, all, none — но в новых версиях всегда all или none, remote удалён
`-Xshare:autoonoff`
-Xloggc:<file>Логирование GC в файл (устарело в Java 9+, заменено -Xlog:gc*)-Xloggc:gc.log
-XprofВстроенный профайлер (устарело в Java 14, удалено в 15)
-Xdebug, -XrunjdwpПоддержка JDWP (устарело, заменено -agentlib:jdwp)-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
-Xcomp, -Xint, -XmixedРежим JIT-компиляции-Xcomp — компилировать всё сразу; -Xint — интерпретировать всё; -Xmixed — по умолчанию (гибрид)

1.3. Экспериментальные -XX параметры

Эти параметры доступны через -XX:+OptionName, -XX:-OptionName, -XX:OptionName=value.

1.3.1. Память и GC

ПараметрОписаниеЗначения / Примеры
-XX:+UseSerialGCПоследовательный GC (однопоточный)Подходит для малых heap и одноядерных сред
-XX:+UseParallelGCParallel GC («пропускная способность»)По умолчанию в Oracle JDK ≤ 8 и в режиме -server
-XX:+UseParallelOldGCВключает Parallel Old для старшего поколения (актуально только если используется Parallel GC)Включается автоматически при -XX:+UseParallelGC
-XX:+UseConcMarkSweepGCCMS GC (устарело в Java 9, удалено в 14)
-XX:+UseG1GCG1 GC (Garbage-First)По умолчанию в Java 9–16
-XX:+UseZGCZ Garbage CollectorТребует -XX:+UnlockExperimentalVMOptions до Java 15; начиная с Java 15 — без флага
-XX:+UseShenandoahGCShenandoah GCОбычно требует сборки OpenJDK с поддержкой Shenandoah
-XX:MaxGCPauseMillis=<ms>Целевая максимальная пауза GC (влияет на G1, Z, Shenandoah)-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=<size>Размер региона G11m, 2m, 4m, 8m, 16m, 32m (по умолчанию вычисляется)
-XX:G1NewSizePercent=5, -XX:G1MaxNewSizePercent=60Мин/макс доля heap под молодое поколение в G1Проценты от общей heap
-XX:InitiatingHeapOccupancyPercent=45Порог запуска concurrent cycle в G1Процент заполнения heap
-XX:+AlwaysPreTouchВыделение и «трогание» всей heap при стартеУменьшает фрагментацию, увеличивает время старта
-XX:+UseCompressedOopsИспользование сжатых указателей (64-битный режим)Включено по умолчанию при heap ≤ 32 ГБ
-XX:ObjectAlignmentInBytes=<n>Выравнивание объектов (только с +UseCompressedOops)Степень двойки, ≥ 8, ≤ 256; по умолчанию 8
-XX:+UseLargePagesИспользование huge pages (требует ОС-поддержки)+UseLargePages + настройка vm.nr_hugepages в Linux

1.3.2. JIT и компиляция

ПараметрОписание
-XX:+TieredCompilationМногоуровневая компиляция (C1 + C2) — по умолчанию с Java 8
-XX:TieredStopAtLevel=1..4Уровень остановки компиляции (1 — интерпретатор + C1 без профилирования, 4 — C2)
-XX:CompileThreshold=10000Кол-во вызовов метода до компиляции C1 (для -XX:-TieredCompilation)
-XX:ReservedCodeCacheSize=240mМакс. размер кэша скомпилированного кода
-XX:+PrintCompilationВывод информации о компиляции в stderr
-XX:+UnlockDiagnosticVMOptionsРазблокировка диагностических опций (для -XX:+PrintAssembly, -XX:+LogCompilation, и др.)
-XX:+PrintAssemblyДизассемблирование скомпилированного кода (требует hsdis библиотеки)
-XX:CICompilerCount=<n>Количество потоков JIT-компилятора
-XX:+BackgroundCompilationФоновая компиляция (по умолчанию включена)

1.3.3. Class Data Sharing (CDS)

ПараметрОписание
-XX:SharedArchiveFile=<file>Путь к архиву CDS (classes.jsa)
-XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=app.jsa -Xshare:dumpГенерация кастомного архива
-XX:ArchiveClassesAtExit=<file>Автоматическое создание архива при завершении JVM

1.3.4. JFR (Java Flight Recorder)

ПараметрОписание
-XX:+FlightRecorderВключение JFR (по умолчанию включён в OpenJDK ≥ 11)
-XX:StartFlightRecording=settings=<profile>,filename=<file.jfr>,duration=60sЗапуск записи при старте
-XX:FlightRecorderOptions=repository=<dir>,maxsize=1gНастройка хранилища и лимитов

1.3.5. Отладка и диагностика

ПараметрОписание
-XX:+HeapDumpOnOutOfMemoryErrorСоздание heap-дампа при OutOfMemoryError
-XX:HeapDumpPath=/path/to/dump.hprofПуть для heap-дампа
-XX:ErrorFile=/path/to/hs_err_pid%p.logЛог ошибок JVM при крахе
-XX:+PrintGC, -XX:+PrintGCDetails, -XX:+PrintGCDateStampsПодробное логирование GC (устарело в Java 9+, заменено -Xlog:gc*)
-XX:+PrintCommandLineFlagsВывод всех нестандартных флагов при старте
-XX:+TraceClassLoading, -XX:+TraceClassUnloadingТрассировка загрузки/выгрузки классов
-XX:+UseGCLogFileRotation, -XX:NumberOfGCLogFiles=5, -XX:GCLogFileSize=10MРотация GC-логов (устарело, заменено -Xlog:gc*:file=gc.log:time,filecount=5,filesize=10M)

1.4. Современное логирование GC и JVM через -Xlog (Java 9+)

Синтаксис:
-Xlog:[<selectors>][:[output][:[decorators][:output-options]]]

Селекторы (что логировать):

  • gc: сборка мусора
  • gc+age=trace, gc+heap=debug, gc+metaspace=info, jit, class, safepoint, os, jfr, monitorinflation, modules
  • all=trace: всё на уровне trace

Выходы:

  • stdout, stderr, file=filename, file=filename%t (временная метка), file=filename%p (PID)

Декораторы:

  • time, utctime, uptime, timemillis, timenanos, pid, tid, level, tags

Примеры:

# Полный GC-лог с ротацией
-Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=5,filesize=20M

# JIT-компиляция и инлайнинг
-Xlog:jit+compilation=info,jit+inlining=debug:file=jit.log

# Загрузка модулей и классов
-Xlog:class+load=info,modules=info:stdout

# JFR-события в реальном времени (требует JFR)
-Xlog:jfr*=trace:stdout

2. Системные свойства среды выполнения

Системные свойства — это ключ-значение пары, доступные внутри JVM через System.getProperty(String key). Они формируются из нескольких источников и имеют иерархию приоритетов:

  1. Значения по умолчанию, заданные реализацией JVM (например, os.name, java.version);
  2. Параметры -Dkey=value, переданные при запуске;
  3. Программные вызовы System.setProperty(String key, String value) — действуют только после выполнения, не влияют на уже инициализированные подсистемы;
  4. Переменные окружения — не становятся системными свойствами автоматически, но некоторые свойства считывают их значения при инициализации (например, java.home зависит от JAVA_HOME, если JVM запущена через java launcher).

Системные свойства можно разделить по функциональным группам.


2.1. Свойства, описывающие среду выполнения

Эта группа фиксирована, доступна только для чтения, устанавливается JVM при старте.

СвойствоОписаниеПример значения
java.versionВерсия Java (JRE)17.0.12
java.runtime.versionПолная версия JRE (включая сборку)17.0.12+7-Ubuntu-122.04
java.vm.versionВерсия JVM (HotSpot)17.0.12+7-Ubuntu-122.04
java.vm.vendorПоставщик JVMUbuntu, Eclipse Adoptium, Azul Systems, Inc.
java.vm.nameНазвание JVMOpenJDK 64-Bit Server VM
java.vm.specification.versionВерсия спецификации JVM17
java.specification.versionВерсия спецификации Java SE17
java.specification.vendorПоставщик спецификацииOracle Corporation
java.vendorПоставщик реализации JREEclipse Adoptium
java.vendor.urlСсылка на сайт поставщикаhttps://adoptium.net/
java.homeКорневой каталог JRE/usr/lib/jvm/java-17-openjdk-amd64
java.class.versionВерсия формата .class61.0 (Java 17 → 61, Java 21 → 65)

2.2. Свойства операционной системы и аппаратуры

СвойствоОписаниеПример значения
os.nameНазвание ОСLinux, Windows 10, Mac OS X
os.versionВерсия ОС5.15.0-101-generic, 10.0, 14.5
os.archАрхитектура процессораamd64, aarch64, x86, ppc64le
sun.arch.data.modelРазрядность данных (32/64)64
sun.cpu.isalistПоддерживаемые инструкции CPU (x86)sse4.2,aes
sun.cpu.endianПорядок байтовlittle, big

⚠️ sun.* свойства непереносимы — их отсутствие возможно в альтернативных реализациях (например, GraalVM Native Image).


2.3. Пользователь и локаль

СвойствоОписаниеЗначенияПримечание
user.nameИмя текущего пользователя ОСtimur, AdministratorБерётся из системного вызова getpwuid / GetUserName
user.homeДомашняя директория/home/timur, C:\Users\Timur
user.dirТекущая рабочая директория при запуске/opt/app, D:\projectМеняется при System.setProperty("user.dir", …) не влияет на File без абсолютного пути — поведение не определено
user.languageЯзык локали по умолчаниюen, ru, zhМожет отличаться от Locale.getDefault().getLanguage() после изменения локали
user.country, user.regionСтрана/регионUS, RU, CN
user.variantВариант локалиPOSIX, WIN, EURO
file.separatorРазделитель пути/, \Константа, не меняется
path.separatorРазделитель путей в CLASSPATH:, ;
line.separatorСимвол(ы) конца строки\n, \r\n

2.4. Свойства ввода-вывода и кодировок

СвойствоОписаниеЗначения по умолчаниюПримечание
file.encodingКодировка по умолчанию для FileReader, FileWriter, PrintStream, PrintWriter без явной кодировкиUTF-8 (Java ≥ 18), ранее ANSI_X3.4-1968 (ASCII) или системнаяВажно: в Java 17 и ранее это могло быть Cp1251 на Windows-локали ru_RU. С Java 18 — всегда UTF-8, если не переопределено через -Dfile.encoding=…
sun.stdout.encoding, sun.stderr.encodingКодировка System.out и System.errТо же, что file.encoding, но может быть переопределено отдельноУчитываются только при создании PrintStream внутри JVM, до первого использования System.out
native.encodingКодировка ОС для нативных вызовов (например, имен файлов)UTF-8, Cp1251, KOI8-RДоступно с Java 17+, используется в Path.of(String) и Files при работе с не-UTF-8 именами
sun.jnu.encodingКодировка для аргументов командной строки и имён файлов в JNIТо же, что native.encoding

2.5. Свойства сети

Эти свойства влияют на поведение java.net API.

СвойствоОписаниеТипичные значения
http.proxyHostХост HTTP-проксиproxy.company.com, 192.168.1.10
http.proxyPortПорт HTTP-прокси3128, 8080
https.proxyHost, https.proxyPortТо же для HTTPS
http.nonProxyHostsСписок хостов, к которым прокси не применяется`localhost
ftp.proxyHost, ftp.proxyPortFTP-прокси
socksProxyHost, socksProxyPortSOCKS-прокси (версия 5)
java.net.useSystemProxiesИспользовать системные настройки прокси (только на Windows/macOS)true, false
networkaddress.cache.ttlВремя жизни кэша DNS-имён (секунды)-1 — кэшировать вечно, 30 — 30 сек, 0 — не кэшировать
networkaddress.cache.negative.ttlTTL для отрицательных DNS-запросов10 по умолчанию
sun.net.inetaddr.ttlУстаревшее, эквивалентно networkaddress.cache.ttl
java.net.preferIPv4StackИспользовать IPv4 при наличии дуального стекаtrue, false
java.net.preferIPv6AddressesПредпочитать IPv6-адрес при разрешении имёнtrue, false
jdk.net.URLClassPath.disableNonFileURLsЗапрет загрузки классов по не-file URLtrue, false

⚠️ Прокси-настройки применяются только к HttpURLConnection, URL.openConnection(). Клиенты вроде HttpClient (Java 11+) или OkHttp не используют их — требуют явной конфигурации ProxySelector.


2.6. Свойства безопасности

СвойствоОписание
java.security.managerВключение SecurityManager (устарело в Java 17, удалено в 18)
java.security.policyПуть к файлу политики безопасности (актуально только при наличии SecurityManager)
java.security.auth.login.configФайл конфигурации JAAS
java.security.krb5.confПуть к krb5.conf для Kerberos
javax.net.ssl.trustStore, javax.net.ssl.trustStoreType, javax.net.ssl.trustStorePasswordПараметры доверенного хранилища
javax.net.ssl.keyStore, javax.net.ssl.keyStoreType, javax.net.ssl.keyStorePasswordПараметры клиентского хранилища ключей
javax.net.ssl.keyStoreProviderПровайдер хранилища
javax.net.debugОтладка SSL/TLS соединений

2.7. Свойства локализации и форматирования

СвойствоОписаниеПример
user.timezoneЧасовой пояс по умолчаниюUTC, Europe/Moscow
java.locale.providersПорядок провайдеров локализации (Java 9+)COMPAT,SPI,CLDR,JRE — приоритет от левого к правому
java.util.PropertyResourceBundle.encodingКодировка .properties файловISO-8859-1 (по умолчанию), UTF-8 (начиная с Java 9, если файл содержит  BOM или указано явно)

🔹 Начиная с Java 9, PropertyResourceBundle поддерживает UTF-8 без экранирования, если файл сохранён в UTF-8 с BOM или указано -Djava.util.PropertyResourceBundle.encoding=UTF-8.


2.8. Свойства параллелизма

СвойствоОписаниеЗначение по умолчанию
java.util.concurrent.ForkJoinPool.common.parallelismРазмер ForkJoinPool.commonPool()Runtime.getRuntime().availableProcessors() - 1, минимум 1
java.util.concurrent.ForkJoinPool.common.exceptionHandlerКласс обработчика исключений (FQCN)
java.util.concurrent.ForkJoinPool.common.threadFactoryКласс ThreadFactory (FQCN)

Пример:

-Djava.util.concurrent.ForkJoinPool.common.parallelism=4
-Djava.util.concurrent.ForkJoinPool.common.threadFactory=com.example.CustomThreadFactory

2.9. Свойства GUI (AWT/Swing)

СвойствоОписаниеПримеры
awt.toolkitКласс AWT Toolkitsun.awt.X11.XToolkit, sun.awt.windows.WToolkit
swing.defaultlafLook-and-Feel по умолчаниюjavax.swing.plaf.metal.MetalLookAndFeel, com.sun.java.swing.plaf.gtk.GTKLookAndFeel
swing.metalThemeТема Metal L&Fsteel, ocean
awt.useSystemAAFontSettingsСглаживание шрифтовon, off, gasp, lcd, lcd_hrgb
swing.aatextВключение сглаживания в Swingtrue, false
sun.java2d.openglИспользовать OpenGL для рендерингаTrue, False
sun.java2d.d3dИспользовать Direct3D (Windows)True, False
sun.java2d.noddrawОтключить DirectDraw (Windows)true, false

🔹 Эти свойства влияют только на приложения с GUI. На серверных JVM (headless) они игнорируются.


2.10. Свойства, управляемые переменными окружения

Некоторые системные свойства инициализируются на основе переменных окружения при старте JVM:

Переменная окруженияВлияющее свойствоПримечание
JAVA_HOMEjava.homeТолько если java запущен как $JAVA_HOME/bin/java
CLASSPATHjava.class.pathПереопределяется опцией -cp или -classpath
TMPDIR (Unix), TEMP, TMP (Windows)java.io.tmpdirПриоритет: TMPDIR > TEMP > TMP
LANG, LC_ALL, LC_CTYPEuser.language, user.country, file.encoding, sun.jnu.encodingJVM парсит локаль из LANG=en_US.UTF-8user.language=en, user.country=US, file.encoding=UTF-8

Пример:

LANG=ru_RU.UTF-8 LC_ALL=ru_RU.UTF-8 java -jar app.jar
# → user.language=ru, user.country=RU, file.encoding=UTF-8

2.11. Динамические свойства: System.setProperty

Метод System.setProperty(String key, String value) изменяет значение свойства во время выполнения. Однако:

  • Не все подсистемы реагируют на изменения после инициализации (например, file.encoding игнорируется после первого создания InputStreamReader);
  • Свойства безопасности (например, java.security.policy) не перечитываются;
  • Изменение user.dir не влияет на поведение new File("file.txt") — относительные пути разрешаются на уровне ОС при вызове open(), а не внутри JVM.

Рекомендуется устанавливать конфигурационные свойства до инициализации зависимых компонентов.


3. Конфигурация безопасности

Подсистема безопасности Java включает несколько взаимосвязанных компонентов:

  • Java Cryptography Architecture (JCA) — криптографические сервисы;
  • Java Secure Socket Extension (JSSE) — TLS/SSL;
  • Java Authentication and Authorization Service (JAAS) — аутентификация и авторизация;
  • Java Generic Security Services (JGSS) — Kerberos, SPNEGO;
  • Policy-based и Module-based ограничения доступа — управление разрешениями;
  • Security Manager — устаревший механизм контроля доступа (удалён в Java 18);
  • Security Properties — глобальные настройки через java.security.

Все настройки управляются через:

  • файл $JAVA_HOME/conf/security/java.security (ранее $JAVA_HOME/lib/security/java.security);
  • файлы политики: java.policy, java.security.policy, пользовательские .policy;
  • системные свойства (-D…);
  • программные вызовы (Security.addProvider, Policy.setPolicy);
  • параметры запуска (-Djava.security.manager, -Djava.security.policy).

3.1. Файл java.security

Расположение:

  • $JAVA_HOME/conf/security/java.security (Java ≥ 9)
  • $JAVA_HOME/lib/security/java.security (Java ≤ 8)

Это основной конфигурационный файл JVM. Он содержит директивы вида key=value, комментарии (#), и поддерживает переменные (${java.home}).

3.1.1. Провайдеры криптографии (security.provider.N)

Порядок провайдеров определяет приоритет при поиске реализации алгоритмов.

security.provider.1=SUN
security.provider.2=SunRsaSign
security.provider.3=SunEC
security.provider.4=SunJSSE
security.provider.5=SunJCE
security.provider.6=SunJGSS
security.provider.7=SunSASL
security.provider.8=XMLDSig
security.provider.9=SunPCSC
security.provider.10=JDKPSS # начиная с Java 11

Наиболее важные провайдеры:

ПровайдерОписание
SUNРеализация MessageDigest (SHA-1, SHA-256), SecureRandom, Signature (SHA1withDSA), KeyStore (JKS), Policy
SunRsaSignПодписи RSA: SHA256withRSA, SHA512withRSA
SunECКриптография на эллиптических кривых: EC, ECDH, ECDSA, secp256r1, secp384r1
SunJSSEРеализация JSSE: SSLContext, KeyManagerFactory, TrustManagerFactory
SunJCEСимметричные алгоритмы: AES, DES, DESede, HmacSHA256, PBE, Cipher (GCM, CBC), KeyGenerator, SecretKeyFactory
SunJGSSGSS-API/Kerberos v5

Добавление стороннего провайдера (например, Bouncy Castle):

security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider

Или программно:

Security.insertProviderAt(new BouncyCastleProvider(), 1);

3.1.2. Алгоритмы, отключённые по умолчанию

ПараметрОписаниеПример значения
jdk.tls.disabledAlgorithmsАлгоритмы, запрещённые в TLSSSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC
jdk.certpath.disabledAlgorithmsАлгоритмы, запрещённые при проверке сертификатовMD2, MD5, SHA1 jdkCA & usage TLSServer, RSA keySize < 1024, DSA keySize < 1024
jdk.jar.disabledAlgorithmsАлгоритмы, запрещённые в подписях JARТо же, что certpath, плюс SHA1 при определённых условиях
jdk.security.provider.preferredПредпочтительные провайдеры для алгоритмовSHA256withRSA:SUN, AES/GCM/NoPadding:SunJCE

Формат значений — список условий через запятую. Поддерживает:

  • keySize < N, keySize > N
  • & usage … — применение (например, & usage TLSServer)
  • & jdkCA — если сертификат выпущен доверенным CA из cacerts

Пример:

jdk.tls.disabledAlgorithms=SSLv3, TLSv1, RC4, DH keySize < 2048, EC keySize < 256

3.1.3. Генератор случайных чисел

ПараметрОписание
securerandom.sourceИсточник энтропии для SecureRandom.getInstanceStrong()
securerandom.strongAlgorithmsАлгоритмы, считающиеся «сильными»

🔹 new SecureRandom() без аргументов использует NativePRNG (на базе /dev/urandom) — неблокирующий.
🔹 SecureRandom.getInstanceStrong() использует NativePRNGBlocking (на базе /dev/random) — может блокироваться при нехватке энтропии.

3.1.4. Хранилища сертификатов

ПараметрОписаниеЗначение по умолчанию
javax.net.ssl.trustStoreСистемное хранилище доверенных сертификатов${java.home}/lib/security/cacerts
javax.net.ssl.trustStoreTypeТип хранилищаJKS, PKCS12 (по умолчанию JKS)
javax.net.ssl.trustStoreProviderПровайдер хранилищаSUN
javax.net.ssl.keyStoreКлиентское хранилище ключей
ssl.KeyManagerFactory.algorithmАлгоритм KeyManagerFactorySunX509, PKIX
ssl.TrustManagerFactory.algorithmАлгоритм TrustManagerFactoryPKIX, SunX509

Файл cacerts содержит корневые сертификаты Let’s Encrypt, DigiCert, GlobalSign и др. Его можно обновить через keytool -importcert.

3.1.5. Политики безопасности

ПараметрОписание
policy.providerКласс реализации Policy
policy.url.1, policy.url.2, …Список URL файлов политики
policy.allowSystemPropertyРазрешить подстановку системных свойств в .policy
policy.expandPropertiesРазрешить ${…} в путях в .policy

3.1.6. JAAS (аутентификация)

ПараметрОписание
login.configuration.providerКласс загрузчика конфигурации JAAS
java.security.auth.login.configПуть к jaas.conf

3.1.7. Отладка

СвойствоЭффект
-Djava.security.debug=…Включает детальное логирование подсистемы
Возможные значения:
accessПроверки SecurityManager (устаревшее)
certpathПостроение и проверка цепочки сертификатов
jarПроверка подписей JAR
logincontextJAAS: логин-модули, callback
configfileЗагрузка jaas.conf
providerРегистрация провайдеров
pkcs11Работа с PKCS#11
allВсё

Пример:

-Djava.security.debug=certpath,logincontext

3.2. Файлы политики (.policy)

Формат:

grant [CodeSource] [, Principal ...] {
Permission permission_class ["target"] [, "action"] [, signedBy "signer"];
};

3.2.1. Элементы grant

  • CodeSource: источник кода — codeBase "URL" + опционально signedBy "alias".
    • codeBase "file:/opt/app/" — локальные классы;
    • codeBase "http://example.com/" — удалённые;
    • codeBase "file:${user.home}/" — переменные разрешаются.
  • Principal: субъект (JAAS), например Principal com.sun.security.auth.UnixPrincipal "timur".

3.2.2. Типы разрешений (Permission)

КлассОписаниеПример
java.io.FilePermissionДоступ к файловой системеnew FilePermission("/tmp/-", "read,write,delete")
java.net.SocketPermissionСетевые соединенияnew SocketPermission("example.com:443", "connect,resolve")
java.lang.RuntimePermissionОперации среды выполненияnew RuntimePermission("exitVM"), "getClassLoader", "setFactory", "accessClassInPackage.sun.misc"
java.lang.reflect.ReflectPermissionРефлексияnew ReflectPermission("suppressAccessChecks")
java.security.SecurityPermissionИзменение security-подсистемыnew SecurityPermission("putProviderProperty.SunJCE")
java.util.PropertyPermissionЧтение/запись системных свойствnew PropertyPermission("file.encoding", "read")
java.net.NetPermissionСетевые настройкиnew NetPermission("specifyStreamHandler"), "getProxySelector"
javax.sound.sampled.AudioPermissionДоступ к аудиоnew AudioPermission("play")
javax.security.auth.AuthPermissionJAAS-операцииnew AuthPermission("createLoginContext.myContext")
java.security.AllPermissionПолный доступ (только для доверенного кода)new AllPermission()

Шаблоны в FilePermission и SocketPermission:

  • /tmp/-/tmp и все подкаталоги;
  • /tmp/* — только файлы в /tmp, без подкаталогов;
  • "*.example.com:443" — все поддомены на порту 443;
  • "localhost:1024-" — порты ≥ 1024.

3.2.3. Пример файла политики

grant codeBase "file:/opt/app/lib/-" {
permission java.io.FilePermission "/tmp/app.log", "write";
permission java.net.SocketPermission "api.example.com:443", "connect,resolve";
permission java.util.PropertyPermission "user.timezone", "read";
permission java.lang.RuntimePermission "accessDeclaredMembers";
};

grant codeBase "file:${java.home}/lib/ext/-" {
permission java.security.AllPermission;
};

grant Principal com.sun.security.auth.UnixPrincipal "timur" {
permission javax.security.auth.AuthPermission "doAs";
};

Загрузка:

java -Djava.security.manager -Djava.security.policy==/opt/app/app.policy MyApp

(два = означают только этот файл, один = — добавить к системным).


3.3. JAAS: jaas.conf

Формат:

AppName {
com.sun.security.auth.module.KeyStoreLoginModule required
keyStoreURL="file:/keystore.jks"
keyStorePassword="secret";

com.sun.security.auth.module.UnixLoginModule sufficient;
};

Типы модулей:

  • required — должен завершиться успешно, но не прерывает цепочку при ошибке;
  • requisite — при ошибке — сразу отказ;
  • sufficient — при успехе — цепочка завершается успешно;
  • optional — игнорируется, если другие модули успешны.

Встроенные модули:

МодульНазначение
KeyStoreLoginModuleАутентификация по ключу из JKS/PKCS12
UnixLoginModuleАутентификация через PAM (Linux/macOS)
NTLoginModuleАутентификация в Active Directory (Windows)
Krb5LoginModuleАутентификация через Kerberos

Пример Kerberos:

KafkaClient {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
keyTab="/etc/security/keytabs/app.keytab"
principal="app/host@REALM"
storeKey=true
useTicketCache=false;
};

3.4. JSSE: TLS/SSL-конфигурация

3.4.1. Программная настройка SSLContext

KeyStore trustStore = KeyStore.getInstance("JKS");
try (InputStream in = Files.newInputStream(Paths.get("trust.jks"))) {
trustStore.load(in, "changeit".toCharArray());
}

TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(trustStore);

SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, tmf.getTrustManagers(), null);

3.4.2. Системные свойства для JSSE

СвойствоОписание
https.protocolsПротоколы для HttpsURLConnection
https.cipherSuitesРазрешённые шифронаборы
jdk.tls.client.protocolsПротоколы для всех клиентских сокетов
jdk.tls.server.protocolsПротоколы для серверных сокетов
jdk.tls.ephemeralDHKeySizeРазмер ключа DH для обмена
jdk.tls.namedGroupsРазрешённые эллиптические кривые

3.4.3. Отладка TLS

-Djavax.net.debug=ssl:handshake:keygen:verbose

Уровни: ssl, handshake, keygen, session, defaultctx, all.


3.5. Устаревшее: SecurityManager

  • Включался через -Djava.security.manager (Java ≤ 17);
  • Все проверки checkPermission() вызывали SecurityManager.check…();
  • Удалён в Java 18: вызов System.setSecurityManager() выбрасывает UnsupportedOperationException;
  • Рекомендуемая замена: модульные ограничения (--limit-modules, --add-exports, --add-opens), AccessController.doPrivileged (остался, но без SecurityManager работает как no-op), проверки на уровне приложения/контейнера.

3.6. Криптографические ограничения: JCE Jurisdiction Policy Files

До Java 8 требовались отдельные US_export_policy.jar, local_policy.jar для неограниченной криптографии.
Начиная с Java 8u161 и Java 9 — неограниченные политики включены по умолчанию. Проверка:

int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
System.out.println(maxKeyLen); // 2147483647 → неограничено

4. Логирование

В Java логирование реализуется через иерархию фасадов и движков. Основные компоненты:

  • API — интерфейсы (Logger, Level, Handler);
  • Фасады — абстракции над движками (SLF4J);
  • Движки — реализации (java.util.logging, Logback, Log4j 2);
  • Мосты — перенаправление вызовов между движками (jul-to-slf4j, log4j-to-slf4j);
  • Форматы и транспортыPatternLayout, JSON, GELF, syslog, Kafka, HTTP;
  • Контекст — MDC (Mapped Diagnostic Context), NDLC (Nested Diagnostic Logical Context).

4.1. java.util.logging (JUL)

Встроен в Java SE. Конфигурация — через $JAVA_HOME/conf/logging.properties (Java ≥ 9) или программно.

4.1.1. Иерархия логгеров

  • Корневой логгер: "" (пустая строка), эквивалентен Logger.getLogger("");
  • Иерархия строится по точкам: com.example.service → родитель com.examplecom"";
  • Уровень логгера наследуется от ближайшего предка с установленным уровнем.

4.1.2. Уровни (Level)

УровеньЧисловое значениеНазначение
OFFInteger.MAX_VALUEОтключение логирования
SEVERE1000Критические ошибки (исключения, сбои)
WARNING900Предупреждения (потенциально некорректное поведение)
INFO800Информационные сообщения (старт, завершение, ключевые события)
CONFIG700Конфигурационная информация (версии, параметры)
FINE500Отладка уровня сервиса
FINER400Подробная отладка
FINEST300Наиболее детальная отладка
ALLInteger.MIN_VALUEВсё

Программное управление:

Logger logger = Logger.getLogger("com.example");
logger.setLevel(Level.FINE);

4.1.3. Обработчики (Handler)

КлассОписание
ConsoleHandlerВывод в System.err
FileHandlerЗапись в файл, поддерживает ротацию (%u, %g, %t)
StreamHandlerПроизвольный OutputStream
SocketHandlerОтправка в TCP-сокет (устаревший, без TLS)
MemoryHandlerБуферизация в памяти с триггером сброса
AsyncHandler (Java 19+)Асинхронная обработка (экспериментальный)

Параметры FileHandler:

java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 5242880 # 5 МБ
java.util.logging.FileHandler.count = 5 # 5 файлов
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.FileHandler.append = true

4.1.4. Форматтеры (Formatter)

КлассФормат вывода
SimpleFormatterмар 13, 2025 10:05:30 ДП com.example.Main main WARNING: Message
XMLFormatterXML-структура лог-записи
LogRecord.toString()Сырой вывод (для отладки)

Кастомный SimpleFormatter через шаблон (Java 7+):

java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL %4$-7s [%3$s] %5$s%6$s%n

Где:
%1$t — время (LogRecord.getMillis),
%2$s — имя логгера,
%3$s — имя потока,
%4$s — уровень,
%5$s — сообщение,
%6$s — исключение (если есть),
%n — системный перевод строки.

4.1.5. Фильтры (Filter)

Интерфейс java.util.logging.Filter:

public boolean isLoggable(LogRecord record) {
return !record.getMessage().contains("password");
}

Устанавливается на логгер или хендлер:

logger.setFilter(filter);
consoleHandler.setFilter(filter);

4.1.6. Глобальная конфигурация: logging.properties

Расположение: $JAVA_HOME/conf/logging.properties
Пример:

handlers=java.util.logging.ConsoleHandler,java.util.logging.FileHandler
.level=INFO

java.util.logging.ConsoleHandler.level=WARNING
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

com.example.level=FINE
com.example.handlers=java.util.logging.FileHandler
com.example.useParentHandlers=false

Загрузка альтернативного файла:

-Djava.util.logging.config.file=/opt/app/logging.properties

4.2. SLF4J (Simple Logging Facade for Java)

Фасад, не реализующий логирование, а делегирующий вызовы движку.

4.2.1. API

Logger logger = LoggerFactory.getLogger(MyClass.class);

logger.trace("Detailed trace");
logger.debug("Debug info: {}", value);
logger.info("Operation started");
logger.warn("Potential issue: {}", cause);
logger.error("Failure", exception);

Методы:

  • trace(), debug(), info(), warn(), error() — 12 перегрузок на каждый уровень (с объектами, массивами, исключениями);
  • Поддержка параметризации: {} заменяется String.valueOf(arg);
  • Поддержка Supplier<String> (Java 8+): logger.debug(() -> computeExpensiveMessage()).

4.2.2. Привязка к движку

SLF4J выбирает движок по наличию JAR-файла META-INF/services/org/slf4j/impl/StaticLoggerBinder:

JARДвижок
slf4j-simple-*.jarПростая реализация (вывод в System.err, уровень INFO)
slf4j-jdk14-*.jarПривязка к java.util.logging
logback-classic-*.jarLogback (рекомендуется)
slf4j-log4j12-*.jarLog4j 1.2 (устарело)
log4j-slf4j-impl-*.jarLog4j 2

Только один StaticLoggerBinder должен быть в classpath — иначе ошибка инициализации.

4.2.3. Мосты

АртефактНазначение
jul-to-slf4jПеренаправление JUL → SLF4J (через SLF4JBridgeHandler)
log4j-over-slf4jЗамена Log4j 1.2 API → SLF4J
jcl-over-slf4jЗамена Jakarta Commons Logging → SLF4J

Активация JUL → SLF4J:

SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();

4.3. Logback

Реализация SLF4J, разработанная тем же автором (Ceki Gülcü). Конфигурация: logback.xml, logback.groovy, logback-test.xml.

4.3.1. Структура logback.xml

<configuration scan="true" scanPeriod="30 seconds">
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>app.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{ISO8601} [%thread] %-5level %logger{40} [%X{traceId}] - %msg%n</pattern>
</encoder>
</appender>

<logger name="com.example.service" level="DEBUG" additivity="false">
<appender-ref ref="FILE"/>
</logger>

<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>

4.3.2. Appenders

КлассОписание
ConsoleAppenderВывод в System.out/System.err
FileAppenderЗапись в файл (без ротации)
RollingFileAppenderФайл с ротацией (требует RollingPolicy)
SMTPAppenderОтправка email при ERROR/WARN
DBAppenderЗапись в БД (таблицы logging_event, logging_event_property, logging_event_exception)
SyslogAppenderОтправка в syslog (RFC 3164/5424)
KafkaAppender (через logback-kafka-appender)Отправка в Kafka
AsyncAppenderОборачивает другие appenders в асинхронную очередь

4.3.3. Rolling Policies

КлассКритерий ротации
TimeBasedRollingPolicyПо времени (%d{yyyy-MM-dd})
SizeBasedTriggeringPolicyПо размеру (maxFileSize)
SizeAndTimeBasedRollingPolicyПо времени + размеру (%i — индекс внутри дня)
FixedWindowRollingPolicyФиксированные окна (app.log, app.log.1, …)

4.3.4. Encoders

ТипОписание
PatternLayoutEncoderШаблонный вывод (аналог SimpleFormatter)
JsonEncoder (через logback-json-classic)Вывод в JSON (поля: timestamp, level, logger, message, thread, mdc)

Пример JSON-шаблона:

<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<logLevel/>
<loggerName/>
<threadName/>
<message/>
<mdc/>
<stackTrace/>
</providers>
</encoder>

4.3.5. Фильтры

КлассОписание
ThresholdFilterПропускать записи ≥ уровня
LevelFilterТочный уровень (ACCEPT/NEUTRAL/DENY)
EvaluatorFilter + JaninoEventEvaluatorУсловия на Groovy/Janino:
<expression>logger.contains("service") &amp;&amp; message.contains("timeout")</expression>
MDCFilterФильтр по MDC-ключам

4.3.6. MDC и NDLC

  • MDC.put("traceId", "abc123") — карта, привязанная к потоку;
  • В шаблоне: %X{traceId};
  • NDLC.push("DB_CALL"), NDLC.pop() — стек для логической вложенности;
  • В шаблоне: %x — вся цепочка через разделитель.

4.4. Log4j 2

Современный движок, несовместимый с Log4j 1.x. Конфигурация: log4j2.xml, log4j2.json, log4j2.yaml, log4j2.properties.

4.4.1. Структура log4j2.xml

<Configuration status="WARN" monitorInterval="30">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>

<RollingFile name="File" fileName="app.log"
filePattern="app.%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout>
<pattern>%d{ISO8601} [%t] %-5level %logger{40} [%X{traceId}] - %msg%n</pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="100MB"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>

<Async name="AsyncFile">
<AppenderRef ref="File"/>
</Async>
</Appenders>

<Loggers>
<Logger name="com.example.service" level="debug" additivity="false">
<AppenderRef ref="AsyncFile"/>
</Logger>

<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="AsyncFile"/>
</Root>
</Loggers>
</Configuration>

4.4.2. Особенности Log4j 2

ФичаОписание
Async LoggersВысокопроизводительное асинхронное логирование через Disruptor (до 10× быстрее)
LookupsДинамические значения: ${env:HOME}, ${sys:user.name}, ${date:yyyy-MM}, ${docker:containerId}
FiltersBurstFilter, DynamicThresholdFilter, ScriptFilter (JavaScript/Groovy), StructuredDataFilter
ReliabilityFailoverAppender, RoutingAppender (маршрутизация по условию)
JSON Template LayoutГотовые шаблоны: EcsLayout, GelfLayout, JsonTemplateLayout
JFR IntegrationАвтоматическая запись событий логирования в JFR-трассу

Пример асинхронного логгера:

<AsyncLogger name="com.example" level="debug" includeLocation="true"/>

Требует com.lmax:disruptor в classpath.

4.4.3. Безопасное логирование

  • Masking: через RegexReplacement в PatternLayout:
    <PatternLayout>
    <pattern>%m%n</pattern>
    <regexReplacement regex="password=([^&amp;]*)"
    replacement="password=***"/>
    </PatternLayout>
  • SensitiveDataFilter (Log4j 2.17+): фильтр по регулярным выражениям для маскировки.

4.5. Сравнение движков

КритерийJULLogbackLog4j 2
ПроизводительностьНизкаяСредняяВысокая (Async Loggers)
Гибкость конфигурацииОграниченнаяВысокаяОчень высокая
АсинхронностьJava 19+ (AsyncHandler)AsyncAppenderAsyncAppender, AsyncLogger
JSON-поддержкаНетЧерез расширенияВстроенная (JsonTemplateLayout)
JMX-мониторингДаДаДа
JFR-интеграцияНетНетДа
ЛицензияGPL+CEEPL 1.0 / LGPL 2.1Apache 2.0

4.6. Рекомендации по использованию

  1. Используйте SLF4J как API — обеспечивает независимость от движка.
  2. Выбирайте Logback или Log4j 2 — JUL не подходит для production-сред.
  3. Включайте MDC для трассировки запросов (traceId, spanId).
  4. Избегайте конкатенации строк — используйте параметризованные сообщения.
  5. Логируйте исключения полностьюlogger.error("msg", exception), а не logger.error("msg: " + ex.getMessage()).
  6. Маскируйте чувствительные данные на уровне appenders.
  7. Ограничьте уровень логирования в productionINFO для root, WARN/ERROR для внешних библиотек.
  8. Используйте ротацию и сжатие — предотвращает заполнение диска.
  9. Не логируйте в System.out/System.err напрямую — нарушает централизованное управление.

5. Конфигурация модульной системы (JPMS)

JPMS введён в Java 9 как механизм компоновки на уровне платформы. Основные цели:

  • надёжная инкапсуляция внутренних API;
  • улучшенная производительность запуска и потребления памяти;
  • предсказуемая сборка и развёртывание.

Модуль описывается в файле module-info.java, компилируется в module-info.class, размещается в корне JAR или директории.


5.1. Синтаксис module-info.java

[open] module <module-name> {
requires [transitive] [static] <module>;
exports <package> [to <module>,];
opens <package> [to <module>,];
uses <service-interface>;
provides <service-interface> with <implementation-class>;
}

5.1.1. Объявление модуля

  • Именованный модуль: имеет module-info.java, имя совпадает с именем JAR-файла (без версии и .jar) или директории.
  • Unnamed module: код без module-info.java — автоматически включает все JAR из classpath, экспортирует все пакеты, требует все модули.
  • Automatic module: JAR без module-info.class, но в --module-path — получает имя по Automatic-Module-Name из MANIFEST.MF или по имени файла (без версии и расширения).

Пример:

module com.example.app {
requires java.base;
requires transitive com.example.core;
requires static org.slf4j;

exports com.example.api;
exports com.example.internal to com.example.test;

opens com.example.config;

uses com.example.spi.DatabaseDriver;
provides com.example.spi.DatabaseDriver with com.example.postgres.PostgresDriver;
}

5.1.2. Директивы

ДирективаОписаниеПримечания
requires <module>Зависимость от модуляБез transitive — зависимость не наследуется
requires transitive <module>Транзитивная зависимостьПотребители модуля автоматически require этот модуль
requires static <module>Опциональная зависимость (только во время компиляции)Во время выполнения модуль может отсутствовать
exports <package>Публикация пакета для чтения и рефлексииТолько public классы и члены доступны
exports <package> to <module>Ограниченная публикация (qualified export)Доступ только указанному модулю
opens <package>Разрешение рефлексивного доступа к пакетуДля всех модулей (аналог --add-opens)
opens <package> to <module>Ограниченный opensТолько для указанного модуля
open module <name>Все пакеты открыты для рефлексииЭквивалентно opens *
uses <service>Объявление использования сервиса через ServiceLoaderНе требует requires модуля, содержащего интерфейс
provides <service> with <impl>Регистрация реализации сервисаКласс должен реализовывать интерфейс и иметь публичный конструктор без аргументов

🔹 exports не даёт доступ к private/package-private через рефлексию — только через opens.
🔹 opens не даёт доступ к private членам без setAccessible(true).
🔹 provides … with требует, чтобы реализация находилась в том же модуле.


5.2. Параметры запуска JVM для JPMS

Все параметры начинаются с --.

ПараметрОписаниеПример
--module-path <path>Поиск модулей (JAR с module-info.class или автоматических)--module-path lib:mods
--class-path <path>Поиск классов в unnamed module--class-path legacy/*
-m <module>[/<main-class>], --module <…>Запуск модуля--module com.example.app/com.example.Main
--add-modules <module>,…Явное включение модулей (даже если не требуется)--add-modules java.xml.bind (Java 9–10, удалён в 11)
--add-exports <source-module>/<package>=<target-module>Динамический exports--add-exports java.base/sun.nio.ch=ALL-UNNAMED
--add-opens <source-module>/<package>=<target-module>Динамический opens--add-opens java.base/java.lang=org.mockito
--patch-module <module>=<path>Замена/дополнение содержимого модуля--patch-module java.base=hotfix/
--limit-modules <module>,…Ограничение набора доступных модулей--limit-modules java.base,java.logging
--list-modulesВывод списка всех наблюдаемых модулейТолько в консоли
--describe-module <module>Описание модуля--describe-module java.base
`--illegal-access=denypermitwarn

5.2.1. Специальные значения для --add-exports / --add-opens

Целевой модульЭффект
ALL-UNNAMEDВсе классы в unnamed module (classpath)
ALL-SYSTEMВсе именованные системные модули
ALL-MODULE-PATHВсе модули из --module-path
java.*, jdk.*Конкретный модуль

Пример для Mockito (требует доступа к private полям):

--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED

5.3. Инструменты JPMS

5.3.1. jdeps — анализ зависимостей

Основное применение: выявление модульных зависимостей и непубличных API.

# Анализ JAR-файла
jdeps --module-path lib app.jar

# Проверка совместимости с модульной системой
jdeps --check MODULE_PATH --multi-release 17 app.jar

# Вывод графа зависимостей (dot-формат)
jdeps -R --generate-module-info mods app.jar

# Поиск зависимостей от internal API
jdeps --jdk-internals app.jar

Важные флаги:

  • -cp, --class-path — classpath (для unnamed module);
  • -mp, --module-path — модульный путь;
  • --print-module-deps — список требуемых модулей (для --add-modules);
  • --generate-open-module — генерация open module для legacy-кода.

Генерирует кастомный runtime image с заданными модулями.

jlink \
--module-path $JAVA_HOME/jmods:mods \
--add-modules com.example.app,java.logging,java.xml \
--output myruntime \
--compress 2 \
--strip-debug \
--no-header-files \
--launcher app=com.example.app/com.example.Main

Флаги:

  • --compress=1 (ZIP), =2 (ZIP + удаление отладочной инфо);
  • --strip-debug — удаление отладочных таблиц;
  • --launcher name=module/class — создаёт bin/name и bin/name.bat;
  • --bind-services — включение реализаций provides … with;
  • --ignore-signing-information — игнорирование подписей JAR.

Результат: myruntime/ содержит bin/java, conf/, lib/, release.

5.3.3. jmod — работа с .jmod-файлами

Формат .jmod — для поставки модулей разработчикам (не для развёртывания).

# Создание .jmod
jmod create \
--class-path classes \
--main-class com.example.Main \
--module-version 1.0 \
--target-platform linux-amd64 \
app.jmod

# Просмотр содержимого
jmod list app.jmod
jmod describe app.jmod

⚠️ .jmod нельзя использовать в --module-path — только в jlink --module-path ….

5.3.4. jimage — работа с modules-образом

Внутри $JAVA_HOME/lib/modules — единый образ всех модулей JRE.

# Просмотр содержимого
jimage list $JAVA_HOME/lib/modules

# Извлечение модуля
jimage extract --dir extracted $JAVA_HOME/lib/modules java.base

# Создание кастомного образа (редко используется напрямую)
jimage create --module-path mods --output custom.jimage

5.4. Динамические модули и ModuleLayer

JPMS поддерживает создание динамических слоёв модулей во время выполнения.

ModuleFinder finder = ModuleFinder.of(Paths.get("mods"));
ModuleLayer parent = ModuleLayer.boot();
Configuration cf = parent.configuration().resolve(finder, ModuleFinder.of(), Set.of("com.example.app"));
ModuleLayer layer = parent.defineModulesWithOneLoader(cf, ClassLoader.getSystemClassLoader());

// Получение ClassLoader модуля
ClassLoader appLoader = layer.findLoader("com.example.app");
Class<?> mainClass = appLoader.loadClass("com.example.Main");

Используется в:

  • плагинных системах;
  • изоляции загрузчиков;
  • тестовых фреймворках (JUnit 5+).

5.5. DIAGNOSTICS: анализ модульной системы

5.5.1. Через JVM

java --describe-module java.base
# → выводит requires, exports, opens, uses, provides

java --list-modules
# → все модули, наблюдаемые из boot layer

java -version --show-version
# → включает модульную информацию при `--show-version`

5.5.2. Через API

Module boot = Object.class.getModule(); // java.base
System.err.println(boot.getDescriptor().name());

// Все наблюдаемые модули
Layer.boot().modules().forEach(m ->
System.out.println(m.getName() + ": " + m.getPackages())
);

5.5.3. Ошибки и их интерпретация

ОшибкаПричинаРешение
module not found: XМодуль X отсутствует в --module-path или не требуется явноДобавить --add-modules X или включить в requires
package Y is declared in module X, but module Z does not read itZ не requires X, и X не exports Y в ZДобавить requires X или --add-exports X/Y=Z
unable to open Y in module XПопытка рефлексии без opensДобавить opens Y или --add-opens X/Y=Z
provides Z with W, but W is not in this moduleРеализация вне модуляПеренести класс или использовать ServiceLoader.load(Z, layer)

5.6. Совместимость с legacy-кодом

  1. Classpath vs Module-path:

    • Classpath → unnamed module → экспортирует всё, требует всё;
    • Module-path → именованные/автоматические модули.
  2. Split package:
    Один пакет в нескольких модулях — запрещено.
    Решение: --patch-module, объединение JAR, рефакторинг.

  3. Reflection на internal API:
    --add-opens java.base/java.lang=ALL-UNNAMED — временное решение.
    Рекомендуется миграция на публичные API.

  4. ServiceLoader в unnamed module:
    Файл META-INF/services/com.example.Spi в JAR на classpath работает без module-info.java.


6. Конфигурация сборки и развёртывания

Цель: формирование самодостаточных, безопасных и переносимых артефактов — от JAR до установщиков операционных систем.


6.1. MANIFEST.MF

Файл META-INF/MANIFEST.MF в JAR-архиве содержит метаданные. Кодировка — UTF-8 (начиная с Java 7), строки не длиннее 72 байт (перенос через пробел в начале строки).

6.1.1. Обязательные атрибуты

АтрибутОписаниеПример
Manifest-VersionВерсия спецификации манифеста1.0
Created-ByИнструмент создания17.0.12 (Eclipse Adoptium)

6.1.2. Атрибуты запуска

АтрибутОписаниеПримечание
Main-ClassПолное имя класса с public static void main(String[])Должен быть в корне JAR или в Class-Path
Start-ClassИспользуется Spring Boot — класс с @SpringBootApplicationНе стандартный, обрабатывается JarLauncher
Launcher-ClassДля модульных JAR — модуль/классcom.example.app/com.example.Main

6.1.3. Атрибуты классов и зависимостей

АтрибутОписаниеПример
Class-PathСписок JAR/директорий (относительно JAR)lib/commons-lang3.jar lib/config/
Multi-ReleaseПоддержка multi-release JARtrue
Automatic-Module-NameИмя модуля для automatic moduleorg.slf4j

⚠️ Class-Path не поддерживает wildcard (*). Каждый JAR указывается явно.

6.1.4. Атрибуты версионирования и поставки

АтрибутОписание
Implementation-Title, Implementation-Version, Implementation-VendorИдентификация реализации (доступны через Package.getImplementation*())
Specification-Title, Specification-Version, Specification-VendorИдентификация спецификации
SealedЗакрытие JAR: все классы должны быть подписаны и из одного источника
PermissionsУровень прав для Web Start (устарело)

6.1.5. Подпись JAR (JAR Signing)

Для каждого пакета или файла — отдельная запись:

Name: com/example/
SHA-256-Digest: abc123...

Name: com/example/MyClass.class
SHA-256-Digest: def456...

Генерируется jarsigner на основе MANIFEST.MF и содержимого.


6.2. Multi-Release JAR (MRJAR)

Позволяет включать разные реализации классов для разных версий Java в один JAR.

Структура:

mylib.jar
├── META-INF/
│ └── MANIFEST.MF ← Multi-Release: true
├── com/example/Util.class ← Java 8+
└── META-INF/versions/
├── 9/com/example/Util.class ← Java 9+
├── 11/com/example/Util.class ← Java 11+
└── 17/com/example/Util.class ← Java 17+

Требования:

  • MANIFEST.MF содержит Multi-Release: true;
  • Классы в versions/N/ должны иметь точно то же полное имя, что и в корне;
  • Нельзя переопределять пакеты — только классы;
  • Поддерживается начиная с Java 9 (JVM выбирает версию ≤ текущей, наибольшую).

Сборка через jar:

jar --create --file mylib.jar \
--main-class com.example.Main \
-C classes8 . \
--release 9 -C classes9 . \
--release 11 -C classes11 . \
--release 17 -C classes17 .

Проверка:

jar --describe-module --file mylib.jar
# или
unzip -l mylib.jar | grep versions

6.3. Sealed JAR

Запрещает загрузку классов из того же пакета, но из другого JAR или источника.

В MANIFEST.MF:

Name: com/example/internal/
Sealed: true

Эффект:
new URLClassLoader(new URL[]{other.jar}).loadClass("com.example.internal.X")SecurityException.

Используется для защиты внутренних API.


6.4. jar — утилита управления архивами

6.4.1. Основные команды

КомандаОписание
jar --create --file app.jar -C classes .Создание JAR из директории
jar --update --file app.jar -C config application.propertiesДобавление файла
jar --extract --file app.jarРаспаковка
jar --list --file app.jarСписок содержимого
jar --describe-module --file app.jarАнализ модульности
jar --print-module-descriptor --file app.jarВывод module-info

6.4.2. Модульные JAR

jar --create --file app.jar \
--main-class com.example.Main \
-C build/classes/module-info.class \
-C build/classes .

Требования:

  • module-info.class в корне;
  • имя JAR совпадает с именем модуля (рекомендуется).

6.5. jpackage — сборка native-установщиков

Генерирует пакеты для ОС: .deb, .rpm, .msi, .pkg, .app, app-image.

6.5.1. Режимы

РежимОписание
--type app-imageСамодостаточная директория (без установщика)
--type deb/rpm/msi/pkg/app-imageУстановщик или образ

6.5.2. Основные параметры

ПараметрОписаниеПример
--input <dir>Директория с JAR и зависимостями--input libs
--main-jar <jar>Главный JAR--main-jar app.jar
--main-class <class>Класс main (если не в MANIFEST.MF)--main-class com.example.Main
--name <name>Имя приложения--name MyApp
--app-version <ver>Версия--app-version 1.2.0
--vendor <name>Поставщик--vendor "Example Inc."
--description <text>Описание--description "My Java App"
--icon <file>Иконка (.ico, .png, .icns)--icon app.ico
--linux-shortcut, --win-shortcut, --mac-package-identifierОС-специфичные опции

6.5.3. Сценарий: сборка .deb с runtime

# 1. Сборка runtime через jlink
jlink \
--module-path $JAVA_HOME/jmods:libs \
--add-modules com.example.app,java.logging \
--output runtime \
--strip-debug \
--compress 2

# 2. Подготовка input: JAR + runtime
mkdir -p package/{app,bin}
cp app.jar package/app/
cp -r runtime/* package/bin/

# 3. Сборка DEB
jpackage \
--type deb \
--input package/app \
--main-jar app.jar \
--runtime-image package/bin \
--name myapp \
--app-version 1.0.0 \
--vendor "Example" \
--description "My App" \
--linux-shortcut

Результат: myapp_1.0.0-1_amd64.deb, устанавливаемый через dpkg -i.

⚠️ jpackage требует установленных системных утилит:

  • Linux: dpkg, rpmbuild;
  • Windows: WiX Toolset (3.11+);
  • macOS: pkgbuild, productbuild.

6.6.1. Оптимизация образа

ФлагДействие
--strip-debugУдаление отладочной информации (line number tables, local variables)
--compress=2Сжатие class-файлов (ZIP + stripping)
--no-header-filesУдаление include/ (заголовки JNI)
--no-man-pagesУдаление man-страниц
--strip-native-commandsУдаление jcmd, jinfo, jmap и др. (остаётся только java)
`--endian littlebig`
`--vm serverclient

6.6.2. Кастомизация release-файла

Файл release в корне образа содержит:

JAVA_VERSION="17.0.12"
MODULES="java.base java.logging com.example.app"
SOURCE="…"
IMPLEMENTOR="Eclipse Adoptium"

Можно дополнить через --release-info-file custom.properties.


6.7. jdeps — расширенный анализ

6.7.1. Генерация module-info.java

jdeps --generate-module-info mods app.jar
# → mods/com.example.app/module-info.java

Автоматически определяет:

  • requires — по использованию классов;
  • exports — по public классам, используемым извне;
  • opens — если обнаружено использование рефлексии.

6.7.2. Выявление недостающих зависимостей

jdeps --suggest-missing-deps app.jar
# → [NOT FOUND] requires java.xml.bind (inaccessible)

6.7.3. Проверка multi-release-совместимости

jdeps --multi-release 17 --class-path libs app.jar

6.8. Подпись JAR: jarsigner и keytool

6.8.1. Генерация ключей

# 1. Создание keystore
keytool -genkeypair \
-alias mykey \
-keyalg RSA \
-keysize 4096 \
-sigalg SHA384withRSA \
-dname "CN=Timur Tagirov, O=IT Universe, C=RU" \
-validity 3650 \
-keystore keystore.jks \
-storepass changeit \
-keypass changeit

6.8.2. Подпись

jarsigner \
-keystore keystore.jks \
-storepass changeit \
-keypass changeit \
-sigalg SHA384withRSA \
-digestalg SHA-384 \
-tsa http://timestamp.digicert.com \
app.jar mykey

Флаги:

  • -tsa — сервер временных меток (требуется для валидности после истечения срока ключа);
  • -verbose — детальный вывод;
  • -certs — включить цепочку сертификатов.

6.8.3. Проверка подписи

jarsigner -verify -verbose -certs app.jar

Вывод включает:

  • действительность подписи;
  • цепочку сертификатов;
  • наличие временной метки;
  • соответствие политики (jdk.jar.disabledAlgorithms).

6.9. Рекомендации по развёртыванию

  1. Используйте jlink + jpackage для production-дистрибутивов — уменьшает размер, исключает ненужные модули.
  2. Подписывайте JAR, если требуется проверка целостности (например, в enterprise-средах).
  3. Избегайте Class-Path с абсолютными путями — нарушает переносимость.
  4. Для библиотек — указывайте Automatic-Module-Name в MANIFEST.MF, даже если нет module-info.java.
  5. Multi-release JAR — только при необходимости — усложняет сборку и отладку.
  6. Sealed JAR — для внутренних SDK, где критична изоляция пакетов.

7. Конфигурация инструментов JDK

Инструменты JDK делятся на:

  • Сборочные: javac, javadoc, jar, jmod;
  • Диагностические: jps, jstat, jmap, jstack, jinfo, jcmd;
  • Интерактивные: jshell;
  • Профилировочные: jfr.

Все они поставляются в $JAVA_HOME/bin/. Начиная с Java 11, отдельный JRE удалён — все инструменты входят в стандартный JDK.


7.1. javac — компилятор Java

7.1.1. Основные параметры

ПараметрОписаниеПример
-d <dir>Директория вывода .class-d build/classes
-s <dir>Директория вывода сгенерированных исходников (например, @Generated)-s src/generated
-sourcepath <path>Поиск исходников зависимостей-sourcepath src/main:src/lib
-classpath <path>, -cp <path>Classpath для разрешения ссылок-cp lib/*
--module-path <path>, -p <path>Модульный путь--module-path mods
--module-source-path <path>Исходники модулей (структура modA/src, modB/src)--module-source-path 'modules/*/src'

7.1.2. Целевая совместимость

ПараметрОписание
--release <N>Компиляция для версии N (от 7 до текущей)
-source <N>, -target <N>Устаревшие (до Java 9); --release предпочтительнее — гарантирует корректные bootstrap-классы
--enable-previewИспользование preview-фич текущей версии

🔹 --release 11 эквивалентно:
-source 11 -target 11 -bootclasspath $JAVA_HOME/jmods/java.base.jar (внутренне).

7.1.3. Предупреждения и линтинг (-Xlint)

ФлагОписание
-Xlint:allВсе предупреждения
-Xlint:noneОтключить все
-Xlint:deprecationИспользование @Deprecated
-Xlint:uncheckedНебезопасные операции с generic’ами
-Xlint:varargsПроблемы с T... и Object[]
-Xlint:castИзбыточные приведения типов
-Xlint:fallthroughОтсутствие break в switch
-Xlint:finallyreturn в finally
-Xlint:pathНедостижимые пути
-Xlint:serialОтсутствие serialVersionUID в Serializable
-Xlint:moduleПроблемы модульной системы
-WerrorЛюбое предупреждение → ошибка

Пример:

javac -Xlint:unchecked,deprecation -Werror -d bin src/*.java

7.1.4. Аннотации

ПараметрОписание
-processor <class>Явное указание annotation processor’а
-processorpath <path>Classpath для процессоров
-s <dir>Куда писать сгенерированные исходники
-implicit:noneНе компилировать классы, не указанные явно

7.2. javadoc — генерация документации

7.2.1. Стандартные теги

ТегКонтекстОписание
@param <name>Метод/конструкторОписание параметра
@returnМетодВозвращаемое значение
@throws <Exception>МетодИсключение и условие его возникновения
@see <ref>ЛюбойПерекрёстная ссылка
@link <ref>Внутри описанияИнлайновая ссылка
@since <version>ЛюбойВерсия, в которой появилось
@deprecatedЛюбойУстаревшее API
@implSpecИнтерфейс/абстрактный методТребования к реализации
@implNoteЛюбойЗамечание для реализатора

7.2.2. Параметры генерации

ПараметрОписание
-d <dir>Выходная директория
-sourcepath <path>Поиск исходников
--module-path <path>Модульный путь
--module-source-path <path>Исходники модулей
-public, -protected, -package, -privateУровень видимости
-author, -versionВключать @author, @version
-noindexНе генерировать индекс
-notreeБез иерархии классов
-nohelpБез ссылки на help
-link <url>Внешние javadoc (например, JDK)
-stylesheetfile <css>Кастомный CSS

7.2.3. Модульные отчёты

javadoc \
--module-path mods \
--module com.example.app \
-d docs \
-author -version

Генерирует:

  • module-summary.html — обзор модуля;
  • package-summary.html — по пакетам;
  • Поддержка module-info.java в навигации.

7.2.4. HTML5 и безопасность

Начиная с Java 17, javadoc генерирует только HTML5.
Поддержка --allow-script-in-comments удалена — скрипты в комментариях игнорируются.


7.3. jshell — интерактивный REPL

7.3.1. Запуск и режимы

КомандаЭффект
jshellИнтерактивный режим
jshell script.jshВыполнение скрипта
jshell --execution-timeout 30sТаймаут выражения
jshell -qТихий режим (без приветствия)
jshell --startup DEFAULT, --startup <file>Загрузка стартовых команд

7.3.2. Встроенные команды (начинаются с /)

КомандаОписание
/helpСправка
/listСписок всех введённых фрагментов
/list <id>Конкретный фрагмент
/edit <id>Редактирование в external editor (требует $JSHELLEDITOR или --editor)
/drop <id>Удаление фрагмента
/save filenameСохранение сессии
/open filenameЗагрузка сессии
/envТекущее окружение (classpath, modules, feedback)
`/set feedback verboseconcise
/exitВыход

7.3.3. Особенности

  • Поддержка var, record, switch expressions, текстовых блоков;
  • Автодополнение (Tab);
  • История (, );
  • Мультистрочные выражения (ввод до ;);
  • Возможность импорта пакетов: /import java.util.*.

Пример:

jshell> /set feedback verbose
jshell> class Point { int x, y; Point(int x, int y) { this.x = x; this.y = y; } }
| created class Point

jshell> new Point(1, 2)
| $1 ==> Point@1a2b3c
| x: 1
| y: 2

7.4. jcmd — универсальная управляющая утилита

Выполняет команды в работающей JVM. Единственный инструмент, поддерживающий все диагностические операции в новых версиях Java (остальные (jstack, jmap) — устаревшие, реализованы через jcmd).

7.4.1. Общая структура

jcmd [<pid>|<main-class>|0] <command> [arguments]

0 — все локальные JVM.

7.4.2. Основные команды

КомандаОписание
VM.versionВерсия JVM
VM.command_lineАргументы запуска
VM.flags [-all]Все флаги JVM
VM.set_flag <name> <value>Изменение non-manageable флага (только если manageable = true)
GC.runПринудительный GC (эквивалент System.gc())
GC.run_finalizationЗапуск finalization
Thread.print [-l]Стеки потоков (аналог jstack)
VM.class_hierarchy [-i] [-v]Иерархия классов (требует UnlockDiagnosticVMOptions)
VM.classloader_statsСтатистика загрузчиков
VM.stringtable [-verbose]Интроспекция string pool
VM.symboltableТаблица символов
PerfCounter.printСчётчики производительности
JFR.start [settings=…] [filename=…] [duration=…]Запуск Flight Recorder
JFR.stop [name=…] [filename=…]Остановка и сохранение
JFR.checkСостояние записей
ManagementAgent.start [port=…]Включение JMX (если не был включён при старте)

7.4.3. Примеры

# Список всех JVM
jcmd -l

# Дамп потоков
jcmd 12345 Thread.print

# Запуск JFR на 60 секунд
jcmd 12345 JFR.start name=MyRecording settings=profile duration=60s filename=rec.jfr

# Изменение MaxGCPauseMillis в runtime (если manageable)
jcmd 12345 VM.set_flag MaxGCPauseMillis 200

jcmd — предпочтительный инструмент для production-диагностики.


7.5. jps — список Java-процессов

ФлагОписание
jpsPID + короткое имя main-class/JAR
jps -lПолный путь к main-class или JAR
jps -vАргументы JVM
jps -mАргументы main-метода
jps -qТолько PID

Пример:

$ jps -lv
12345 /opt/app/app.jar -Xmx2g -Dfile.encoding=UTF-8
12346 org.apache.catalina.startup.Bootstrap start

7.6. jstat — статистика GC и JIT

КомандаОписание
jstat -gc <pid> [interval] [count]Сборка мусора (Eden, Survivor, Old, Metaspace)
jstat -gccapacity <pid>Ёмкости поколений
jstat -gcutil <pid>% использования поколений
jstat -compiler <pid>Статистика JIT (методы, время компиляции)
jstat -printcompilation <pid>Последние скомпилированные методы

Пример:

jstat -gcutil 12345 1s 5
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 98.45 76.32 45.12 95.34 92.11 123 2.345 5 1.234 3.579

Где:
S0/S1 — Survivor 0/1,
E — Eden,
O — Old,
M — Metaspace,
CCS — Compressed Class Space,
YGC/YGCT — Young GC count/time,
FGC/FGCT — Full GC count/time,
GCT — Total GC time.


7.7. jmap — анализ памяти

КомандаОписание
jmap -heap <pid>Сводка по heap (поколения, GC, capacity)
jmap -histo <pid>Гистограмма классов (экземпляры, байты)
jmap -histo:live <pid>Только живые объекты (запускает GC)
jmap -dump:live,format=b,file=heap.hprof <pid>Heap-дамп в HPROF-формате

⚠️ jmap приостанавливает JVM на время операции. В production используйте jcmd <pid> GC.run_finalization + jcmd <pid> VM.class_histogram, или JFR.


7.8. jstack — дамп стеков потоков

КомандаОписание
jstack <pid>Стеки всех потоков
jstack -l <pid>+ информация о мониторах и owns
jstack -F <pid>Принудительный дамп (если JVM не отвечает)

Формат вывода:

"main" #1 prio=5 os_prio=0 cpu=123.45ms elapsed=10.00s tid=0x00007f0000000000 nid=0x1234 runnable
java.lang.Thread.State: RUNNABLE
at java.util.HashMap.hash(HashMap.java:339)
at java.util.HashMap.put(HashMap.java:612)
at com.example.Main.main(Main.java:10)

🔹 В Java 8–16 jstack работает напрямую; в Java 17+ — обёртка над jcmd <pid> Thread.print.


7.9. jinfo — конфигурация JVM

КомандаОписание
jinfo <pid>Все системные свойства и флаги
jinfo -sysprops <pid>Только System.getProperty
jinfo -flags <pid>Только -XX флаги
jinfo -flag <name> <pid>Значение конкретного флага
jinfo -flag +<name> <pid>Включить boolean-флаг (если manageable)

⚠️ Большинство флагов — non-manageable и не могут быть изменены в runtime.


7.10. jfr — работа с Flight Recorder

КомандаОписание
jfr status <pid>Активные записи
jfr start <pid> [name=…] [settings=…] [duration=…] [filename=…]Запуск записи
jfr stop <pid> [name=…] [filename=…]Остановка и сохранение
jfr dump <pid> [name=…] [filename=…]Дамп текущего буфера без остановки
jfr print <file.jfr>Текстовый вывод событий
jfr metadata <file.jfr>Описание типов событий
jfr extract <file.jfr> --events <type>Извлечение подмножества событий

Форматы settings:

  • default — минимальная нагрузка;
  • profile — полная (методы, allocation, TLAB, monitor, socket);
  • flightrecorder — кастомный (.jfc-файл).

Пример кастомного .jfc:

<configuration version="2.0">
<event name="jdk.CPULoad">
<setting name="enabled">true</setting>
<setting name="period">1 s</setting>
</event>
</configuration>

Загрузка:

jfr start 12345 settings=cpu.jfc filename=cpu.jfr

8. Spring Boot — внешняя конфигурация

Spring Boot предоставляет унифицированную модель загрузки конфигурации из множества источников, с приоритезацией, профилированием, типобезопасным связыванием и поддержкой внешних систем.

Все конфигурационные свойства доступны через Environment и @ConfigurationProperties.


8.1. Иерархия источников конфигурации

Spring Boot загружает свойства в строго определённом порядке, где более поздние источники переопределяют более ранние:

ПриоритетИсточникОписание
1@TestPropertySourceАннотация на тестах
2Аргументы @SpringBootTest(properties = …)В тестах
3properties атрибут @SpringBootTest, @WebMvcTest и др.
4Аргументы командной строки (--name=value)Через SpringApplication.run(args)
5Свойства из SPRING_APPLICATION_JSON (переменная окружения или системное свойство){"app.port":8081}app.port=8081
6ServletConfig init-параметрыВ веб-приложениях
7ServletContext init-параметры
8JNDI-атрибуты (java:comp/env/)
9System.getProperties()Системные свойства JVM (-D…)
10Системные переменные окруженияAPP_PORT=8081
11RandomValuePropertySourcerandom.int, random.uuid
12Профильные application-{profile}.properties из упакованного JAR
13application.properties из упакованного JAR (classpath:/)
14Профильные application-{profile}.properties из config/ в текущей директории./config/application-prod.properties
15application.properties из config/ в текущей директории./config/application.properties
16Профильные application-{profile}.properties в текущей директории./application-prod.properties
17application.properties в текущей директории./application.properties
18@PropertySource на @Configuration-классахЗагружается до application.properties, но после системных источников 1–11 — важное исключение из порядка
19Конфиги, загруженные через spring.config.importВыполняются в порядке объявления, после application.properties, но до @PropertySource с @DependsOn

✅ Актуально для Spring Boot 2.4+. До 2.4 порядок @PropertySource был выше.


8.2. Синтаксис application.properties и application.yml

8.2.1. application.properties

# Простые значения
server.port=8080
app.name=MyApp
app.version=1.2.0

# Списки
app.features=auth,logging,metrics
app.ports=8080,8081,8082

# Вложенные объекты (точечная нотация)
app.datasource.url=jdbc:postgresql://localhost:5432/mydb
app.datasource.username=admin
app.datasource.password=secret
app.datasource.pool.size=10
app.datasource.pool.timeout=30s

# Случайные значения
app.id=${random.uuid}
app.port=${random.int[8000,9000]}

8.2.2. application.yml

server:
port: 8080

app:
name: MyApp
version: 1.2.0
features:
- auth
- logging
- metrics
ports:
- 8080
- 8081
- 8082
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: admin
password: secret
pool:
size: 10
timeout: 30s

id: ${random.uuid}
port: ${random.int[8000,9000]}

🔹 Spring Boot использует SnakeYAML для парсинга.
🔹 Поддерживаются !!int, !!binary, но не рекомендуются — снижают переносимость.


8.3. Профили (spring.profiles.active)

8.3.1. Активация

СпособПример
Системное свойство-Dspring.profiles.active=prod,metrics
Переменная окруженияSPRING_PROFILES_ACTIVE=prod,metrics
application.propertiesspring.profiles.active=dev
application.ymlspring: profiles: active: dev
ПрограммноSpringApplication.setAdditionalProfiles("test")

8.3.2. Условная загрузка

# application.yml
spring:
config:
activate:
on-profile: dev

logging:
level:
com.example: DEBUG

---
spring:
config:
activate:
on-profile: prod

logging:
level:
com.example: WARN

server:
shutdown: graceful

🔹 --- — разделитель документов в YAML.
🔹 В properties — отдельные файлы: application-dev.properties, application-prod.properties.

8.3.3. Включение профилей из кода

@Profile("integration-test")
@Configuration
public class TestDataSourceConfig {}

8.4. spring.config.import — импорт внешних конфигураций

Введён в Spring Boot 2.4 для замены spring.config.location (устаревшего). Поддерживает:

ПрефиксИсточникПример
optional:classpath:JAR/classpath (необязательный)optional:classpath:config/external.properties
classpath:JAR/classpath (обязательный)classpath:secrets.properties
optional:file:Файл (необязательный)optional:file:/etc/app/config.properties
file:Файл (обязательный)file:./local.conf
configtree:Дерево файлов как свойстваconfigtree:/etc/secrets/
vault:HashiCorp Vault (через Spring Cloud)vault://secret/data/myapp
consul:Consul KV (Spring Cloud)consul:myapp/config
zookeeper:ZooKeeper (Spring Cloud)zookeeper:/config/myapp

8.4.1. configtree — безопасное хранение секретов

Структура:

/etc/secrets/
├── db.username
├── db.password
└── api.token

Содержимое файлов — plain text:

# /etc/secrets/db.username
admin

В application.properties:

spring.config.import=optional:configtree:/etc/secrets/

Результат:
db.username=admin, db.password=secret, api.token=abc123.

Преимущества:

  • файлы могут иметь chmod 600;
  • не попадают в логи при --debug;
  • не требуют шифрования внутри.

8.5. @ConfigurationProperties — типобезопасная конфигурация

8.5.1. Объявление

@ConfigurationProperties(prefix = "app.datasource")
@ConstructorBinding // immutable, требует @EnableConfigurationProperties или @ConfigurationPropertiesScan
public record DataSourceProperties(
String url,
String username,
String password,
Pool pool
) {}

public record Pool(int size, Duration timeout) {}

8.5.2. Активация

@SpringBootApplication
@EnableConfigurationProperties(DataSourceProperties.class)
public class Application {}

Или глобально:

@ConfigurationPropertiesScan("com.example.config")
public class Config {}

8.5.3. Валидация

@ConfigurationProperties(prefix = "app.datasource")
@Validated
public record DataSourceProperties(
@NotBlank String url,
@NotBlank String username,
@NotBlank String password,
@Valid Pool pool
) {}

public record Pool(
@Min(1) @Max(100) int size,
@NotNull @Positive Duration timeout
) {}

Ошибка приведёт к BindValidationException при старте.

8.5.4. Конвертация типов

Поддерживаются:

  • Duration"30s", "5m", PT30S;
  • DataSize"10MB", "2GiB";
  • Charset"UTF-8";
  • Resource"classpath:app.sql", "file:/tmp/data.txt";
  • List<T>, Set<T>, Map<String, T>;
  • Кастомные Converter<T, R> и @ConfigurationPropertiesBinding.

Пример конвертера:

@Component
@ConfigurationPropertiesBinding
public class PasswordConverter implements Converter<String, EncryptedPassword> {
public EncryptedPassword convert(String source) {
return new EncryptedPassword(AES.encrypt(source, key));
}
}

8.6. @Value vs @ConfigurationProperties

Критерий@Value@ConfigurationProperties
ТипизацияСтрогая привязка к типу, но без вложенных объектовПолная поддержка вложенных структур
ВалидацияТолько через SpEL (#{…}), без @ValidПоддержка Bean Validation
МетаданныеНетГенерирует META-INF/spring-configuration-metadata.json для IDE
ПроизводительностьРазбор SpEL при каждом доступеОднократное связывание при старте
ИммутабельностьТолько через final + конструкторПоддержка @ConstructorBinding и record’ов
ТестированиеТребует @TestPropertySourceЛегко заменяется моком

Рекомендация:
@ConfigurationProperties — для групп связанных свойств (DAO, клиенты, настройки модуля);
@Value — для единичных значений (@Value("${app.id}") String id).


8.7. Интеграция с внешними системами

8.7.1. Spring Cloud Config Server

bootstrap.yml (загружается до application.*):

spring:
application:
name: myapp
cloud:
config:
uri: http://config-server:8888
username: client
password: secret
fail-fast: true

Конфиг на сервере: myapp-prod.yml:

app:
datasource:
url: jdbc:postgresql://prod-db:5432/mydb

8.7.2. HashiCorp Vault

spring.config.import=vault://secret/data/myapp
spring.cloud.vault:
host: vault.example.com
port: 8200
scheme: https
authentication: TOKEN
token: s.xxxxx

Данные из secret/data/myappapp.*.

8.7.3. AWS Secrets Manager

Через spring.config.import=configtree:/mnt/secrets/, где /mnt/secrets/ — volume с секретами, примонтированными как файлы (ECS/EKS sidecar-паттерн).


8.8. Расширение конфигурационного конвейера

8.8.1. EnvironmentPostProcessor

Выполняется до обработки application.properties.

public class CustomEnvPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication app) {
Map<String, Object> map = new HashMap<>();
map.put("app.runtime", "kubernetes");
env.getPropertySources().addFirst(new MapPropertySource("custom", map));
}
}

Регистрация в META-INF/spring.factories:

org.springframework.boot.env.EnvironmentPostProcessor=\
com.example.CustomEnvPostProcessor

8.8.2. ConfigDataLocationResolver + ConfigDataLoader

Для кастомных префиксов (например, myconf:).

  1. Реализация ConfigDataLocationResolver<MyConfigDataResource>:

    @Order(HIGHEST_PRECEDENCE)
    public class MyConfigLocationResolver implements ConfigDataLocationResolver<MyConfigDataResource> {
    @Override
    public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
    return location.hasPrefix("myconf:");
    }

    @Override
    public List<MyConfigDataResource> resolve(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
    return List.of(new MyConfigDataResource(location.getNonPrefixedValue()));
    }
    }
  2. Реализация ConfigDataLoader<MyConfigDataResource>:

    public class MyConfigDataLoader implements ConfigDataLoader<MyConfigDataResource> {
    @Override
    public ConfigData load(ConfigDataLoaderContext context, MyConfigDataResource resource) {
    Map<String, Object> props = fetchFromCustomSource(resource.getLocation());
    return new ConfigData(PropertySource.of("myconf", props));
    }
    }
  3. Регистрация в spring.factories:

    org.springframework.boot.context.config.ConfigDataLocationResolver=\
    com.example.MyConfigLocationResolver

    org.springframework.boot.context.config.ConfigDataLoader=\
    com.example.MyConfigDataLoader

8.9. Безопасность конфигурации

8.9.1. Шифрование значений

Jasypt (неофициально, сторонняя библиотека):

app.datasource.password=ENC(G5xJ5s1W3QzK8c9Y1U4aB2vC4nM6lO0p)

Spring Cloud Vault / AWS KMS / Azure Key Vault — предпочтительные production-решения.

8.9.2. Защита от утечек

  • Запрет логирования @ConfigurationProperties с @Sensitive (кастомная аннотация + PropertySourcesDeducer);
  • Использование configtree вместо application.properties для секретов;
  • spring.main.lazy-initialization=true — уменьшает риск преждевременного связывания.

9. Jakarta EE / MicroProfile Config

MicroProfile Config — стандарт конфигурации для микросервисов, часть спецификации MicroProfile. Принят в Jakarta EE как основа для jakarta.config (предварительно — MP Config 3.0+).
Реализации: SmallRye Config (Quarkus, WildFly), Helidon Config, Open Liberty, Payara Micro.


9.1. Основные принципы

  • Декларативность: конфигурация через @ConfigProperty, @Inject Config;
  • Иерархия источников: от высокоуровневых (переменные окружения) к низкоуровневым (файлы);
  • Типобезопасность: автоматическая конвертация в int, Duration, List, Map;
  • Динамичность: поддержка обновления конфигурации без перезапуска (в реализациях);
  • Расширяемость: кастомные ConfigSource, Converter.

9.2. Источники конфигурации и приоритеты

Приоритет определяется целым числом (ordinal). Чем больше ordinal — тем выше приоритет.

Источникordinal по умолчаниюОписание
Programmatically added ConfigSourceУказывается явноНаивысший приоритет при ordinal > 1000
System properties (-Dkey=value)400System.getProperty()
Environment variables300System.getenv()
.env файл в корне проекта295Поддерживается SmallRye, Helidon
microprofile-config.properties в рабочей директории250./microprofile-config.properties
META-INF/microprofile-config.properties в JAR100Упакованный в артефакт
Custom ConfigSource без ordinal100По умолчанию

🔹 Переменные окружения транслируются в свойства по правилу:
APP_DATASOURCE_URLapp.datasource.url (регистронезависимо, _., ___).

Пример:

export APP_DATASOURCE__URL=jdbc:postgresql://prod:5432/mydb
# → app.datasource.url=jdbc:postgresql://prod:5432/mydb

9.3. Формат microprofile-config.properties

Расположение:

  • META-INF/microprofile-config.properties — в JAR;
  • microprofile-config.properties — в текущей директории (для dev-сред).

Синтаксис:

# Простые значения
app.name=MyService
app.version=1.2.0

# Списки
app.ports=8080,8081,8082
app.features=auth;logging;metrics # разделитель можно задать через Config API

# Временные величины
app.timeout=30s
app.delay=5m

# Объём
app.cache.size=100MB

# Секреты (не шифруются по умолчанию)
app.datasource.password=secret

Поддерживаются те же типы, что и в Spring Boot: String, int/Integer, long/Long, boolean/Boolean, Duration, LocalDateTime, URL, URI, Charset, Class, List<T>, Set<T>, Map<String, T>.


9.4. Программное API: Config, ConfigProvider

9.4.1. Получение Config

Config config = ConfigProvider.getConfig();
// или через CDI
@Inject
Config config;

9.4.2. Чтение значений

// Обязательное значение
String name = config.getValue("app.name", String.class);

// Опциональное значение
Optional<Integer> port = config.getOptionalValue("app.port", Integer.class);

// Со значениями по умолчанию
int timeout = config.getValue("app.timeout", int.class, () -> 30);

// Списки
List<Integer> ports = config.getValues("app.ports", Integer.class);

// Map (ключ=значение, по строковому шаблону)
Map<String, String> props = config.getValues("app.datasource", String.class, String.class);
// → app.datasource.url, app.datasource.username → {url=..., username=...}

9.4.3. Интроспекция источников

config.getConfigSources().forEach(source -> {
System.out.println(source.getName() + " (ordinal=" + source.getOrdinal() + ")");
});

9.5. CDI-инъекция: @ConfigProperty

9.5.1. Базовое использование

@ApplicationScoped
public class Service {

@Inject
@ConfigProperty(name = "app.name")
String appName;

@Inject
@ConfigProperty(name = "app.ports")
List<Integer> ports;

@Inject
@ConfigProperty(name = "app.enabled", defaultValue = "true")
boolean enabled;

@Inject
@ConfigProperty(name = "app.timeout")
Duration timeout; // "30s" → Duration.ofSeconds(30)
}

9.5.2. Обработка отсутствующих значений

  • Без defaultValueDeploymentException при старте, если свойство не найдено;
  • С defaultValue — используется значение по умолчанию;
  • Optional<T> — безопасное чтение:
    @Inject
    @ConfigProperty(name = "app.optional.token")
    Optional<String> token;

🔹 @ConfigProperty работает только в CDI-биньках (@ApplicationScoped, @RequestScoped и др.).


9.6. Типобезопасная конфигурация: @ConfigProperties (MicroProfile 3.0+)

Аналог @ConfigurationProperties в Spring Boot.

9.6.1. Объявление

@ConfigProperties(prefix = "app.datasource")
public interface DataSourceConfig {
String url();
String username();
String password();
PoolConfig pool();

@ConfigProperties(prefix = "pool")
interface PoolConfig {
int size();
Duration timeout();
}
}

Или классом:

@ConfigProperties(prefix = "app.datasource")
public class DataSourceConfig {
private String url;
private String username;
private String password;
private PoolConfig pool;

// геттеры/сеттеры или record-конструктор
}

9.6.2. Инъекция

@Inject
DataSourceConfig config;

// Использование
DataSource ds = new DataSource(config.url(), config.username(), config.password());

🔹 В SmallRye Config поддерживается @ConfigMapping (альтернативное имя для @ConfigProperties).
🔹 В Quarkus — @ConfigMapping с поддержкой @WithDefault, @WithParentName, @WithName.


9.7. Расширение: кастомные ConfigSource

9.7.1. Реализация

public class DatabaseConfigSource implements ConfigSource {
private final Map<String, String> properties = loadFromDatabase();

@Override
public int getOrdinal() {
return 500; // выше system props
}

@Override
public Set<String> getPropertyNames() {
return properties.keySet();
}

@Override
public String getValue(String propertyName) {
return properties.get(propertyName);
}

@Override
public String getName() {
return "DatabaseConfigSource";
}
}

9.7.2. Регистрация

Через META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource:

com.example.DatabaseConfigSource

Или программно:

Config config = ConfigProviderResolver.instance()
.getBuilder()
.withSources(new DatabaseConfigSource())
.build();
ConfigProviderResolver.instance().registerConfig(config, getClass().getClassLoader());

⚠️ При программной регистрации — только для текущего ClassLoader.


9.8. Кастомные Converter

9.8.1. Реализация

@Priority(100) // выше встроенных
public class PasswordConverter implements Converter<String> {
@Override
public String convert(String value) {
return AES.decrypt(value, getKey());
}
}

9.8.2. Регистрация

Через META-INF/services/org.eclipse.microprofile.config.spi.Converter:

com.example.PasswordConverter

Или для конкретного типа:

public class DurationConverter implements Converter<Duration> {
@Override
public Duration convert(String value) {
return Duration.parse("PT" + value.toUpperCase());
}
}

→ применяется при config.getValue("key", Duration.class).


9.9. Динамическая конфигурация и события

9.9.1. Поддержка в runtime’ах

  • Quarkus: @ConfigProperty(reactive = true) + @Observes ConfigValueChangeEvent;
  • SmallRye Config: @Inject @ConfigProperty ConfigValue + ConfigValueObserver;
  • Helidon: Config.onChange(…).

9.9.2. Пример (SmallRye)

@ApplicationScoped
public class ConfigObserver {

@Inject
@ConfigProperty(name = "app.feature.toggle")
ConfigValue toggle;

void onConfigChange(@Observes ConfigValueChangeEvent event) {
if ("app.feature.toggle".equals(event.getKey())) {
System.out.println("Feature toggle changed to: " + event.getValue());
}
}
}

🔹 Требует mp.config в pom.xml и активной поддержки в runtime.


9.10. Интеграции с runtime’ами

RuntimeПоддержкаОсобенности
QuarkusПолная (SmallRye Config)application.properties, @ConfigMapping, dev-services, config hot reload в dev mode
HelidonВстроеннаяapplication.yaml, config profiles, programmatic builder
Open LibertyMP Config 3.0+server.xml + microprofile-config.properties, dynamic updates
WildFlySmallRye Configstandalone.xml + META-INF/microprofile-config.properties
Payara MicroMP ConfigПоддержка configsource в payara-micro.properties

9.10.1. Quarkus: расширенные возможности

  • Profile-specific properties: %prod.app.url=…;
  • Config profiles: -Dquarkus.profile=prod;
  • Secrets via Kubernetes ConfigMap/Secret: автоматическое монтирование как configmap:/ и secret:/;
  • Config encryption: через smallrye-config и внешние провайдеры (Vault, KMS).

Пример application.properties в Quarkus:

%dev.app.url=http://localhost:8080
%prod.app.url=https://api.example.com

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${db.user}
quarkus.datasource.password=${db.password}

Переменные db.user, db.password могут поступать из Kubernetes Secret.


9.11. Сравнение с Spring Boot Config

КритерийMicroProfile ConfigSpring Boot Config
СтандартизацияJakarta EE / MicroProfileProprietary (Pivotal/VMware)
Иерархия источниковordinal-приоритетЖёсткий порядок (17+ уровней)
Типобезопасность@ConfigProperties (MP 3.0+)@ConfigurationProperties (с 1.0)
Динамическая перезагрузкаОпционально (runtime-зависимо)Через Spring Cloud Config + @RefreshScope
CDI-интеграцияВстроеннаяТребует Spring Context
Профили%profile.key=valueapplication-{profile}.properties
КонвертацияВстроенная + ConverterВстроенная + Converter/Formatter

✅ MicroProfile Config легче встраивается в non-Spring среды (Quarkus, Helidon, Open Liberty).
✅ Spring Boot Config богаче в enterprise-интеграциях (Vault, Consul, AWS и др. «из коробки»).


10. Конфигурация сетевых и HTTP-клиентов

Java предоставляет два поколения HTTP-клиентов:

  • Legacy: java.net.URL, URLConnection, HttpURLConnection (Java 1.0+);
  • Modern: java.net.http.HttpClient (Java 11+, incubated в 9–10).

Оба поддерживают HTTPS, прокси, аутентификацию, кастомные заголовки. Современный клиент добавляет HTTP/2, reactive streams, строгую типизацию.


10.1. java.net.http.HttpClient (Java 11+)

10.1.1. Создание клиента

HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.followRedirects(HttpClient.Redirect.NORMAL) // NEVER, ALWAYS
.version(HttpClient.Version.HTTP_2) // HTTP_1_1, HTTP_2
.sslContext(sslContext) // кастомный SSLContext
.sslParameters(sslParameters) // кастомные параметры TLS
.executor(executor) // кастомный Executor (по умолчанию ForkJoinPool.commonPool())
.cookieHandler(cookieManager) // управление cookie
.authenticator(authenticator) // HTTP-аутентификация
.proxy(proxySelector) // выбор прокси
.build();

10.1.2. connectTimeout

  • Применяется к connect() сокета;
  • Не влияет на readTimeout (устанавливается в HttpRequest.Builder);
  • Значение Duration.ZERO — отключает таймаут.

10.1.3. followRedirects

ЗначениеПоведение
HttpClient.Redirect.NEVERНе следовать за редиректами (3xx)
HttpClient.Redirect.NORMALСледовать за GET/HEAD, не за POST/PUT/DELETE (стандарт RFC 7231)
HttpClient.Redirect.ALWAYSСледовать за всеми (опасно для неидемпотентных методов)

10.1.4. Версии HTTP

ВерсияПоддержка
HTTP_1_1Всегда доступна
HTTP_2Доступна только при HTTPS и поддержке ALPN на стороне сервера
Авто (null)Попытка HTTP/2 → откат на HTTP/1.1

🔹 HTTP/2 требует TLS 1.2+ и ALPN (Application-Layer Protocol Negotiation).
🔹 В JDK 11+ ALPN встроен в sun.security.ssl.ALPNExtension.

10.1.5. Асинхронные операции

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.timeout(Duration.ofSeconds(30)) // read timeout
.header("User-Agent", "MyApp/1.0")
.GET()
.build();

// Блокирующий вызов
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

// Асинхронный вызов (CompletableFuture)
CompletableFuture<HttpResponse<String>> future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

10.1.6. Тела запросов

ТипМетодПример
СтрокаBodyPublishers.ofString("text")POST("text", BodyPublishers.ofString(body))
ФайлBodyPublishers.ofFile(path)PUT(BodyPublishers.ofFile(Paths.get("data.json")))
ПотокBodyPublishers.ofInputStream(() -> in)POST(BodyPublishers.ofInputStream(() -> new ByteArrayInputStream(bytes)))
ReactiveBodyPublishers.fromPublisher(publisher)Для Flow API

10.1.7. Обработка ответов

ОбработчикОписание
BodyHandlers.ofString()Строка (по умолчанию UTF-8)
BodyHandlers.ofInputStream()InputStream (ленивая загрузка)
BodyHandlers.ofFile(path)Сохранение в файл
BodyHandlers.discarding()Игнорирование тела
BodyHandlers.ofLines()Stream<String> по строкам
BodyHandlers.fromLineSubscriber(subscriber)Reactive streams

10.2. HttpURLConnection / URLConnection (legacy)

10.2.1. Базовое использование

URL url = new URL("https://api.example.com/data");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(10_000);
conn.setReadTimeout(30_000);
conn.setRequestProperty("User-Agent", "MyApp/1.0");

int status = conn.getResponseCode();
String body;
try (InputStream in = conn.getInputStream()) {
body = new String(in.readAllBytes(), StandardCharsets.UTF_8);
}

10.2.2. Настройки подключения

МетодОписание
setConnectTimeout(int ms)Таймаут установки TCP-соединения
setReadTimeout(int ms)Таймаут между байтами при чтении
setUseCaches(boolean)Использовать кэш (только для GET, если ResponseCache установлен)
setChunkedStreamingMode(int chunklen)Отправка тела чанками (для больших POST)
setFixedLengthStreamingMode(int len)Фиксированная длина тела (Content-Length)
setInstanceFollowRedirects(boolean)Следовать редиректам (по умолчанию true)

10.2.3. Кэширование

ResponseCache.setDefault(new MyResponseCache());

// MyResponseCache extends ResponseCache
public CacheResponse get(URI uri, String rqstMethod, Map<String, List<String>> rqstHeaders) {}
public CacheRequest put(URI uri, URLConnection conn) throws IOException {}

Поддерживается только для GET, HEAD.


10.3. Прокси-настройки

10.3.1. Глобальные настройки

Через системные свойства:

-Dhttp.proxyHost=proxy.local -Dhttp.proxyPort=3128
-Dhttps.proxyHost=proxy.local -Dhttps.proxyPort=3128
-Dhttp.nonProxyHosts="localhost|127.0.0.1|*.intranet"

10.3.2. Программные настройки

Для HttpClient:

ProxySelector proxySelector = ProxySelector.of(new InetSocketAddress("proxy.local", 3128));
HttpClient client = HttpClient.newBuilder()
.proxy(proxySelector)
.build();

Или кастомный:

ProxySelector customSelector = new ProxySelector() {
@Override
public List<Proxy> select(URI uri) {
if (uri.getHost().endsWith(".intranet")) {
return List.of(Proxy.NO_PROXY);
}
return List.of(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.local", 3128)));
}

@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
logger.warn("Proxy failed for {}: {}", uri, sa, ioe);
}
};

Для HttpURLConnection:

Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.local", 3128));
HttpURLConnection conn = (HttpURLConnection) new URL("https://example.com").openConnection(proxy);

10.4. TLS/SSL-конфигурация

10.4.1. Создание SSLContext

KeyStore trustStore = KeyStore.getInstance("JKS");
try (InputStream in = Files.newInputStream(Paths.get("trust.jks"))) {
trustStore.load(in, "changeit".toCharArray());
}

TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(trustStore);

SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, tmf.getTrustManagers(), null);

10.4.2. SSLParameters

SSLParameters params = sslContext.getDefaultSSLParameters();

// Протоколы
params.setProtocols(new String[]{"TLSv1.2", "TLSv1.3"});

// Шифронаборы
params.setCipherSuites(new String[]{
"TLS_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
});

// ALPN (для HTTP/2)
params.setApplicationProtocols(new String[]{"h2", "http/1.1"});

// Hostname verification
params.setEndpointIdentificationAlgorithm("HTTPS"); // включает проверку CN/SAN

🔹 setEndpointIdentificationAlgorithm("HTTPS") — аналог HttpsURLConnection.getDefaultHostnameVerifier().

10.4.3. Отключение проверки сертификатов (только для тестов!)

TrustManager[] trustAll = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAll, new SecureRandom());

HttpClient client = HttpClient.newBuilder()
.sslContext(sslContext)
.sslParameters(new SSLParameters())
.build();

❌ Запрещено в production.


10.5. Аутентификация

10.5.1. Authenticator (глобальный)

Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if ("api.example.com".equals(getRequestingHost())) {
return new PasswordAuthentication("user", "pass".toCharArray());
}
return super.getPasswordAuthentication();
}
});

Поддерживает:

  • Basic;
  • Digest;
  • NTLM (на Windows);
  • Negotiate (Kerberos/SPNEGO, если настроен JAAS).

10.5.2. Явные заголовки

String credentials = Base64.getEncoder().encodeToString("user:pass".getBytes(UTF_8));
HttpRequest request = HttpRequest.newBuilder()
.header("Authorization", "Basic " + credentials)
.GET()
.build();

10.5.3. OAuth2 / Bearer

HttpRequest request = HttpRequest.newBuilder()
.header("Authorization", "Bearer " + accessToken)
.GET()
.build();

10.6.1. CookieHandler

CookieManager cookieManager = new CookieManager(
new FileCookieStore(Paths.get("cookies.txt")), // кастомный хранилище
CookiePolicy.ACCEPT_ORIGINAL_SERVER
);

HttpClient client = HttpClient.newBuilder()
.cookieHandler(cookieManager)
.build();

Политики:

  • CookiePolicy.ACCEPT_ALL — принимать все;
  • CookiePolicy.ACCEPT_NONE — игнорировать;
  • CookiePolicy.ACCEPT_ORIGINAL_SERVER — только от исходного домена (без 3rd-party).

10.6.2. Ручное управление

HttpResponse<String> loginResponse = client.send(loginRequest, BodyHandlers.ofString());
String cookies = loginResponse.headers().firstValue("Set-Cookie").orElse("");

HttpRequest dataRequest = HttpRequest.newBuilder()
.header("Cookie", cookies)
.GET()
.build();

10.7. HTTP/2 и ALPN

10.7.1. Требования для HTTP/2

  • HTTPS (TLS 1.2+);
  • Поддержка ALPN на сервере;
  • SSLParameters.setApplicationProtocols(new String[]{"h2", "http/1.1"});
  • JDK ≥ 9 (ALPN встроен).

10.7.2. Проверка версии

HttpResponse<Void> response = client.send(request, BodyHandlers.discarding());
System.out.println(response.version()); // HTTP_2 или HTTP_1_1

10.7.3. HTTP/3 (QUIC)

  • В JDK не поддерживается (на 2025 год);
  • Требует сторонних библиотек: AHC (Async HTTP Client), Jetty HttpClient, Netty.

10.8. Безопасность: OCSP, CRL, Hostname Verification

10.8.1. Включение OCSP stapling

Security.setProperty("ocsp.enable", "true");

10.8.2. Проверка отзыва сертификатов

System.setProperty("com.sun.net.ssl.checkRevocation", "true");
Security.setProperty("jdk.security.certpath.revocationCheck", "PREFER_CRLS");

Возможные значения:

  • PREFER_CRLS — сначала CRL, потом OCSP;
  • PREFER_OCSP — наоборот;
  • NO_FALLBACK — без отката.

10.8.3. Hostname verification

  • По умолчанию в HttpsURLConnection и HttpClientвключена при setEndpointIdentificationAlgorithm("HTTPS");
  • Проверяет:
    • SubjectAlternativeName (SAN) DNS-имена;
    • при отсутствии SAN — CommonName (CN) в SubjectDN;
    • нечувствительно к регистру;
    • поддерживает wildcard (*.example.comapi.example.com, но не a.b.example.com).

10.9. Диагностика и отладка

10.9.1. Логирование SSL

-Djavax.net.debug=ssl:handshake:verbose

10.9.2. Трассировка HTTP

HttpClient client = HttpClient.newBuilder()
.build();

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/get"))
.header("User-Agent", "Debug")
.GET()
.build();

HttpResponse<String> response = client.send(request, BodyHandlers.ofString());

System.out.println("Status: " + response.statusCode());
System.out.println("Headers: " + response.headers().map());
System.out.println("Body: " + response.body());

10.9.3. Перехват запросов/ответов

Через HttpClient.Builder.executor() и кастомный Flow.Subscriber, или через прокси (mitmproxy, Charles).